Spring Test

原文链接:

https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testing-introduction

测试

Spring团队明确提倡采用TDD的方式进行软件开发,因此在单元测试的最佳实践的同时也涵盖了spring对集成测试的支持。spring团队已经发现正确的使用IoC确实可以简化单元测试和集成测试(在IoC中类里面的setter方法和合理的构造器的存在,能够使得类很容易组装在一个测试用例中,而不用手动的将service注册(或者其他的类似操作)在一起。这一章仅仅沉浸在测试能够给你提供的方便上。

1.简介

测试是企业软件开发的一个完整的组成部分。这个章节聚焦于IoC的原则为单元测试提供的附加价值,和获得Spring框架为集成测试的支持的好处上。(对企业应用软件完整的测试治理机制远远超出于这个参照手册的范围。)

2.单元测试

相对于传统的J2EE开发方式,DI能够减少代码对容器的依赖。构成应用的POJOs应该在Junit或TestNG中具备可测试性,这种可测试性仅仅是通过使用new操作进行对象实例化,而不需要依赖于Spring或者其他的容器。你可以通过mock对象的方式,对代码进行独立的测试。如果您遵循Spring的架构建议,那么代码库的清晰分层和组件化将简化单元测试。例如,你可以通过打桩或者mock Dao接口的方式来测试你的service层,而不需要在运行单元测试的时候获取持久层的数据。

因为没有创建运行时环境,真正意义上的单元测试能够以相当快的方式运行。强调一下,作为你开发方法论的一部分,真正的单元测试能够有效的提升你的生产力。你可能不需要这个测试的章节来帮助你针对你的基于IoC的应用编写有效的测试用例。但是本章这种描述了在某些单元测试的场景,Spring框架提供了mock对象和测试支持类。

2.1 mock对象

Spring提供了一组专注于mock的包:

  • Environment
  • JNDI
  • Servlet API
  • Spring Web Reactive

2.1.1 Environment

org.springframework.mock.env包提供了对Environment and PropertySource这两个抽象mock实现。MockEnvironment and MockPropertySource 在针对特定环境的代码,开发脱离容器的测试用例的时候非常实用。

2.1.2 JNDI

org.springframework.mock.jndi包含了JNDI SPI的实现,通过它你可以为Test suits和stand-alone应用构建一个简单的JNDI的环境 。例如,如果JDBC数据源实例绑定了J2EE容器相同的JNDI的名字,那么你可以在测试场景中,在没有修改的情况下,复用代码和配置。

2.1.3. Servlet API

org.springframework.mock.web包包含了一个综合的Servlet API mock对象的集合,这可以用于测试web contexts, controllers, 和filters. 与通过 EasyMock 进行动态的mock对象或者通过MockObjects进行完整的Servlet API的mock对象相比,这些通过使用spring web mvc框架mock的对象通常更加方便。

从spring5开始,org.springframework.mock.web包中mock的对象是基于servlet 4.0的标准。

Spring MVC Test framework基于mock Servlet API objects 为Spring MVC提供了一个完成的测试框架。详情参见 Spring MVC Test.

2.1.4. Spring Web Reactive

未完待续......

2.2 单元测试支持类

Spring提供了一组来支持单元测试,它们分布在两个不同的目录中。

2.2.1 General Testing Utilities

未完待续......

2.2.2. Spring MVC Testing Utilities

3.集成测试

这个章节(占了大量的摄于篇幅)覆盖了Spring应用的集成测试。它包括以下的主题:

  • 概述
  • 集成测试的目标
  • JDBC测试支持
  • 注解
  • Spring TestContext Framework
  • Spring MVC Test Framework
  • PetClinic Example

3.1 概述

未完待续......

3.2 集成测试的目标

Spring集成测试支持包括如下几个目标:

  • 管理测试用例之间Spring IoC容器的缓存
  • 提供测试目标实例的依赖注入
  • 为集成测试提供事务管理
  • 为开发者在编写集成测试用例是提供spring特性的基础类
    以下的四个小结将阐述每一个目标,并且提供相应的实现细节和配置细节的链接。

3.2.1 管理测试用例之间Spring IoC容器的缓存

Test classes typically declare either an array of resource locations for XML or Groovy configuration metadata — often in the classpath — or an array of annotated classes that is used to configure the application. These locations or classes are the same as or similar to those specified in web.xml or other configuration files for production deployments.

By default, once loaded, the configured ApplicationContext is reused for each test. Thus, the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term “test suite” means all tests run in the same JVM — for example, all tests run from an Ant, Maven, or Gradle build for a given project or module. In the unlikely case that a test corrupts the application context and requires reloading (for example, by modifying a bean definition or the state of an application object) the TestContext framework can be configured to reload the configuration and rebuild the application context before executing the next test.

See Context Management and Context Caching with the TestContext framework.
Spring TestContext Framework包含spring ApplicationContext实例和WebApplicationContext实例的加载,同时还包括了上下文的缓存。对加载上下文的缓存支持是十分重要的,因为启动的时间可以变为issue级别,当然这不是因为覆盖整个spring本身,而是因为spring的IoC容器对对象的实例化是要消耗时间的。例如一个项目包含50到100个Hibernater映射文件可能需要消耗10到20秒来加载映射文件,并且需要注意的是,在运行每一个测试用了之前的代价导致导致减慢所有的测试用例的运行,这将降低开发人员的生产力。

3.2.2 提供测试目标实例的依赖注入

3.2.3 为集成测试提供事务管理

3.2.4 为开发者在编写集成测试用例是提供spring特性的基础类

3.3 JDBC测试支持

未完待续......

3.4 注解

未完待续......

3.5 Spring TestContext Framework

未完待续......

3.6 Spring MVC Test Framework

Spring MVC Test framework通过适用于JUnit、TestNG或者其他测试框架的流式api,为测试Spring MVC的代码提供了支持. 这是基于 spring-test模块的 Servlet API mock objects ,因此不需要运行Servlet容器。它使用DispatcherServlet 提供完整的Spring MVC运行时行为,并且在standalone模式的基础上,通过TestContext framework 为加载实际的Spring 配置提供支持,standalone模式中controllers能够手动实例化并且每次只测试一个。

Spring MVC Test 也为测试 RestTemplate的代码提供客户端的支持。客户端测试mock了server端的相应,并且不需要运行服务端。

Spring Boot 提供了一个完整的运行server的端到端集成测试的选项。如果你也有相同的诉求,请移步到 Spring Boot reference page。了解更多的关于out-of-container 和end-to-end integration 测试, 详见 Differences between Out-of-Container and End-to-End Integration Tests.

3.6.1. Server-Side Tests

通过JUnit或TestNG为Controller写一个简单的测试用例很容易 : 简单的实例化Controller、注入mock对象或者打桩的依赖、传递MockHttpServletRequest, MockHttpServletResponse调用方法等等。但是,写这样一个单元测试用例依然有很多内容没有测试,如:request mappings、data binding、type conversion、 validation等等。甚至@InitBinder, @ModelAttribute, and @ExceptionHandler也是在请求处理的生命周期中。
Spring MVC Test的目标是通过真实的DispatcherServlet来执行requests和生成responses,来为测试controller提供一个有效的方式。
这是一个基于JUnit Jupiter的使用Spring MVC Test的例子:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
class ExampleTests {

    private MockMvc mockMvc;

    @BeforeEach
    void setup(WebApplicationContext wac) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    void getAccount() throws Exception {
        this.mockMvc.perform(get("/accounts/1")
                .accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json"))
            .andExpect(jsonPath("$.name").value("Lee"));
    }
}

这个测试用例是基于TestContext framework对WebApplicationContext的支持,来加载同包中的xml配置文件的。当然,Java-based和Groovy-based形式的配置也是支持的,详情请见 sample tests
The jsonPath syntax is supported through the Jayway JsonPath project. Many other options for verifying the result of the performed request are discussed later in this document.
这个MockMvc实例演示了一个/accounts/1的GET请求,并且校验了响应的状态是200, content type是application/json,响应体有一个key=name,value= Lee的JSON属性。jsonPath的语法是基于Jayway JsonPath project的标准。后续的文档将会描述很多其他的验证结果的选项。

Static Imports

前面的测试用例中fluent API需要一些Static Imports,例如 MockMvcRequestBuilders.、 MockMvcResultMatchers.和 MockMvcBuilders.*。 如果使用Eclipse或者基于Eclipse的 Spring Tool Suite,需要在Eclipse的preferences → Java → Editor → Content Assist → Favorites添加为 “favorite static members” 。这样能够在桥下这些api的首字母时,进行相应的辅助提示。 其他IDEs (如:IntelliJ) 可能不需要附加的配置.然后检查static members的代码的自动补全支持。

Setup Choices

有两种方式用于创建MockMvc实例,第一种是通过TestContext framework加载Spring MVC 的配置,这种方式加载Spring的配置并且将WebApplicationContext注入到测试用例中,从而构建MockMvc实例。示例如下:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration("my-servlet-context.xml")
public class MyWebTests {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    // ...

}

第二种方式是在不加字啊spring配置的情况下,手动创建controller实例。基础默认配置自动创建,这相当于MVC JavaConfig 和MVC namespace。可以在一定程度上自义定配置。示例如下:

public class MyWebTests {

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
    }

    // ...

}

到底选择哪种方式呢?
webAppContextSetup的方式加载了真实的Spring MVC 配置,结果就是更加完整的集成测试。因为TestContext framework缓存了Spring 已经加载的配置,这有助于更加快速的测试,即使test suite中有很多的tests。
也可以通过Spring配置将mock Service 注入到 controller中,来保证聚焦在web层的测试。示例如下:

<!--声明mock service-->
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="org.example.AccountService"/>
</bean>
//将mock servcie 注入到 测试用例中。
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration("test-servlet-context.xml")
public class AccountTests {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Autowired
    private AccountService accountService;

    // ...

}

另一方面standaloneSetup的方式更接近于单元测试。这种方式每次只测试一个controller,并且可以手动注入mock依赖到controller中,而不涉及加载Spring配置。这种方式更专注于style,更容易看出正在被测试的controller,不管Sping MVC的配置等是否生效. standaloneSetup的模式对于ad-hoc(点对点)的测试和调试问题是一种非常方便的方式。
关于集成测试还是单元测试的讨论是没有对与错之分的,但是使用standaloneSetup 的方式暗示了补充webAppContextSetup的测试的必要性。总之,可以使用webAppContextSetup的方式,编写全部的测试用例,来保证总是对Spring MVC 进行测试。

Setup Features

Performing Requests

Defining Expectations

Filter Registrations

Differences Between Out-of-Container and End-to-End Integration Tests

Further Server-Side Test Examples

3.6.2. HtmlUnit Integration

3.6.3. Client-Side REST Tests

3.7 PetClinic Example

未完待续......

4. 扩展资源

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容