以spring官方文档为基础,官方地址:Spring Boot_Testing
文章目录
-
- 1. Teting 范围依赖
- 2. 测试Spring应用程序
- 3. 测试Spring Boot应用程序
-
- 3.1 Web应用类型的检测
- 3.2 测试配置的检测
- 3.2 排除测试配置
- 3.3 使用Application参数
- 3.4 使用Mock模拟环境进行测试
- 3.5 使用可运行服务器进行测试
- 3.6 定制WebTestClient
- 3.7 模拟bean
- 3.8 自动配置 测试
- 3.9 自动配置的JSON测试
- 3.10 自动配置的Spring MVC测试
- 3.11 自动配置的Spring WebFlux测试
- 3.12 自动配置的Data JPA测试
- 3.13 自动配置的JDBC测试
- 3.14 自动配置的Data JDBC测试
- 3.15 自动配置的Data Redis测试
- 3.16 自动配置的REST客户端
- 3.17 使用Mock MVC自动配置Spring REST Docs测试
- 3.18 使用WebTestClient自动配置Spring REST Docs测试
- 3.19 使用REST Assured自动配置Spring REST文档测试
- 3.20 自动配置的Spring Web服务 客户端测试
- 3.21 自动配置的Spring Web服务 服务端测试
- 3.22 额外的自动配置及切片
- 4. 测试工具
Spring Boot提供了许多实用工具和注释,可以在测试应用程序时提供帮助。测试支持由两个模块提供:包含核心项的 spring-boot-test,和支持测试自动配置的 spring-boot-test-autoconfigure。
不过大多数开发人员会直接使用spring-boot-starter-test “Starter”,它导入了Spring Boot测试模块以及JUnit Jupiter、AssertJ、Hamcrest和许多其他有用的库。
针对之前有使用JUnit 4,那么可以使用JUnit 5的老式引擎来运行它们。要使用老式引擎,需要在junit-vintage-engine上添加一个依赖项,如下所示:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
为了支持org.hamcrest:hamcrest(spring-boot-starter-test的一部分),需要将hamcrest-core排除。
1. Teting 范围依赖
spring-boot-starter-test " Starter "包含以下提供的库(在测试范围内):
- JUnit 5:Java应用程序单元测试的标准库。
- Spring Test和Spring Boot Test:对Spring Boot应用程序提供的集成测试支持。
- AssertJ:一个流畅的断言库。
- Hamcrest:一个匹配器对象库(也称为约束或谓词)。
- Mockito:Java模拟框架。
- JSONassert:JSON的断言库。
- JsonPath:JSON的XPath。
我们通常发现这些公共库在编写测试时非常有用。如果这些库不适合您的需要,您可以添加您自己的其他测试依赖项。
2. 测试Spring应用程序
依赖项注入的主要优势之一是,它应该使代码更容易进行单元测试。可以使用new操作符来实例化对象,甚至不需要使用Spring。也可以使用mock objects(模拟对象)而不是真正的依赖项。
通常,我们做的不仅仅是简单的单元测试,比如需要使用Spring ApplicationContext的集成测试。而Testing能够在不需要部署应用程序或连接到其他基础设施的情况下执行集成测试。
Spring框架包括一个专门用于此类集成测试的测试模块。可以直接向org.springframework:spring-test声明依赖项,或者使用spring-boot-starter-test“Starter”来传递性拉入。
关于Spring-test模块,更多信息可以从阅读Spring Framework参考文档的相关部分开始。
3. 测试Spring Boot应用程序
Spring Boot应用程序是一个Spring ApplicationContext,所以除了使用普通的Spring上下文之外,不需要做什么特别的测试。
SpringBoot提供了一个@SpringBootTest注释,当需要SpringBoot特性时,可以使用它作为标准Spring -test @ContextConfiguration注释的替代。通过SpringApplication创建测试中使用的ApplicationContext。除了@SpringBootTest,还提供了许多其他注释,用于测试应用程序的其他部分。
如果使用的是JUnit 4,需要在测试中添加@RunWith(SpringRunner.class),否则注释将被忽略。如果使用的是JUnit 5,就不需要添加与@SpringBootTest等价的 @ExtendWith(SpringExtension.class),其他的@…Test注释已经用它进行了注释。
默认情况下,@SpringBootTest不会启动服务器。可以使用@SpringBootTest的webEnvironment属性来进一步优化测试的运行方式:
-
MOCK(默认值):加载web ApplicationContext并提供一个模拟web环境。使用此注释时,嵌入式服务器不会启动。如果一个web环境在你的类路径上是不可用的,那么这个模式会透明地返回到创建一个常规的非web ApplicationContext。它可以与@AutoConfigureMockMvc或@AutoConfigureWebTestClient一起使用,用于基于模拟的web应用程序测试。
-
RANDOM_PORT:加载一个WebServerApplicationContext并提供一个真实的web环境。嵌入式服务器在一个随机端口上启动并监听。
-
DEFINED_PORT:加载WebServerApplicationContext并提供一个真实的web环境。嵌入式服务器将启动并在一个已定义的端口(来自您的应用程序.properties)或默认端口8080上侦听。
-
NONE:通过使用SpringApplication加载ApplicationContext,但不提供任何web环境(模拟或其他)。
针对以上的几种测试模式,需要注意的是:如果测试案例中带有@Transactional,默认情况下,它会在每个测试方法结束时回滚事务。然而,当测试模式是RANDOM_PORT或DEFINED_PORT时,他们会隐式地提供一个真正的servlet环境,HTTP客户端和服务器在不同的线程中运行,也就是在不同的事务中运行。在这种情况下,服务器上发起的任何事务都不会回滚。
@SpringBootTest with webEnvironment = webEnvironment。如果应用程序为管理服务器使用了不同的端口,RANDOM_PORT也将在一个单独的随机端口上启动管理服务器。
3.1 Web应用类型的检测
如果Spring MVC可用,则配置一个常规的基于MVC的应用程序上下文。如果只有Spring WebFlux,Spring Boot会检测它并配置一个基于WebFlux的应用上下文。如果两者都存在,那么Spring MVC优先。
如果想在场景中测试一个响应式web应用程序,那么必须设置spring.main.web-application-type属性:
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
3.2 测试配置的检测
如果熟悉Spring Test框架,可能会习惯使用@ContextConfiguration(classes=…)来指定要加载哪个Spring @Configuration。或者,可能经常在测试中使用嵌套的@Configuration类。
但是在测试Spring Boot应用程序时,通常不需要这样做。如果没有明确定义主配置时,Spring Boot的@*Test注释会自动搜索主配置。
搜索算法从包含测试的包开始工作,直到找到一个用@SpringBootApplication或@SpringBootConfiguration标注的类。只要以合理的方式构造代码,通常就能找到主配置。
如果想定制主配置,可以使用嵌套的@TestConfiguration类。与嵌套的@Configuration类不同(被用来代替应用程序的主配置),嵌套的@TestConfiguration类将用于除应用程序的主配置之外的配置。
3.2 排除测试配置
如果应用程序启用组件扫描(如使用@SpringBootApplication或@ComponentScan),会发现仅为特定测试创建的顶级配置类会意外地在各处被获取。
正如之前看到的,@TestConfiguration可以用于测试的内部类来定制主配置。当放置在顶级类上时,@TestConfiguration表示src/test/java中的类不应该被扫描。然后你可以在需要的地方显式地导入这个类,如下面的例子所示:
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
3.3 使用Application参数
如果需要用到Application 的参数,那么可以使用@SpringBootTest注解,配合args 参数使用:
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
3.4 使用Mock模拟环境进行测试
默认情况下,@SpringBootTest不启动服务器,而是为测试web端点设置一个模拟环境。
- 使用Spring MVC,可以使用MockMvc或WebTestClient来查询web端点,如下面的例子所示:
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
如果你想只关注web层,而不想启动一个完整的ApplicationContext,考虑使用@WebMvcTest代替。
- 使用Spring WebFlux端点,可以使用WebTestClient,如下所示:
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
在模拟环境中进行测试通常比在完整的servlet容器中运行要快。然而,由于模拟发生在Spring MVC层,因此依赖于较低层servlet容器行为的代码不能直接用MockMvc进行测试。
例如,Spring Boot的错误处理是基于servlet容器提供的“错误页面”支持。这意味着,虽然可以测试MVC层抛出和处理异常,却不能直接测试一个特定的自定义错误页面。如果需要测试这些低级别的关注点,可以启动一个完全运行的服务器,如下一节所述。
3.5 使用可运行服务器进行测试
如果需要启动一个完全运行的服务器,Spring Boot建议使用随机端口。如果使用@SpringBootTest(webEnvironment= webEnvironment . random_port),那么每次运行测试时都会随机选择一个可用端口。
可以使用@LocalServerPort注释将实际的端口注入到测试中。为方便起见,需要对已启动的服务器进行REST调用的测试还可以@Autowire一个WebTestClient,它会解析到正在运行的服务器的相对链接,并带有一个用于验证响应的专用API,如下所示:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
WebTestClient可以同时用于真实服务器和模拟环境。
以上设置中需要类路径上有spring-webflux。如果不能或不愿意添加webflux, Spring Boot还提供了一个TestRestTemplate工具:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
3.6 定制WebTestClient
要定制WebTestClient bean,需要配置一个WebTestClientBuilderCustomizer bean。任何由WebTestClient.Builder调用的bean,都会被用于创建WebTestClient。
3.7 模拟bean
在运行测试时,有时有必要在应用程序上下文中模拟某些组件。例如,可能拥有在开发期间不可用的某些远程服务的facade。当您要模拟在真实环境中难以触发的失败时,mock也很有用。
Spring Boot包含一个@MockBean注解,可以用来为ApplicationContext中的bean定义Mockito mock。可以使用注解添加新bean或替换单个现有bean定义。可以直接在测试类、测试中的字段或@Configuration类和字段上使用注释。当在字段上使用时,创建的mock的实例也会被注入。在每个测试方法之后,Mock bean会自动重置。
需注意都是,如果测试时使用SpringBoot相关的的测试注解(例如@SpringBootTest),则会自动启用该特性。如果采用其他不同的方式使用这个特性(如使用@ContextConfiguration),必须显式地添加监听器,如下面的例子所示:
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@ContextConfiguration(classes = MyConfig.class)
@TestExecutionListeners({
MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class })
class MyTests {
// ...
}
下面的例子用一个模拟实现替换了一个现有的RemoteService bean:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@SpringBootTest
class MyTests {
@Autowired
private Reverser reverser;
@MockBean
private RemoteService remoteService;
@Test
void exampleTest() {
given(this.remoteService.getValue(