文章目录以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. 测试工具
- 4.1 ConfigDataApplicationContextInitializer
- 4.2 TestPropertyValues
- 4.3 OutputCapture
- 4.4 TestRestTemplate
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上添加一个依赖项,如下所示:
org.junit.vintage junit-vintage-engine test org.hamcrest hamcrest-core
为了支持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()).willReturn("spring");
String reverse = this.reverser.getReversevalue(); // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}
@MockBean不能用于模拟在应用程序上下文刷新期间执行的bean的行为。在执行测试时,应用程序上下文刷新已经完成,要配置模拟行为已经太迟了。在这种情况下,建议使用@Bean方法来创建和配置mock。
3.8 自动配置 测试Spring Boot的自动配置在应用中运行得很好,但有时对仅仅做一个Test来说有点太过了。只加载测试应用程序的“部分”所需的配置部分通常会比较好一些。例如,可能想要测试Spring MVC控制器是否正确映射url,而不想在这些测试中涉及数据库调用,或者可能想要测试JPA实体,而当这些测试运行时,对web层如何是不感兴趣的。
spring-boot-test-autoconfigure模块包含许多注解,可以用来自动配置这样的“切片”。它们的工作方式都很类似:或提供用于加载ApplicationContext的含@…Test的注解,或提供可用于自定义自动配置的一个或多个含@autoconfigure…的注解。
3.9 自动配置的JSON测试要测试对象的JSON序列化和反序列化是否按照预期工作,可以使用@JsonTest注释。@JsonTest自动配置支持的JSON映射器,它可以是以下库之一:
- Jackson ObjectMapper, any @JsonComponent beans and any Jackson Modules
- Gson
- Jsonb
@JsonTest 支持的自动配置可参考Spring官方附录:
一个@JsonTest示例:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester json;
@Test
void serialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
}
@Test
void deserialize() throws Exception {
String content = "{"make":"Ford","model":"Focus"}";
assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
3.10 自动配置的Spring MVC测试
要测试Spring MVC控制器是否按预期工作,可以使用@WebMvcTest注释。@WebMvcTest自动配置Spring MVC基础架构,并将扫描的bean限制为@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, webmvcregitions,和HandlerMethodArgumentResolver。当使用@WebMvcTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。但@enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。
如果需要注册额外的组件,比如Jackson模块,可以通过在测试中使用@import来导入额外的配置类。
通常,@WebMvcTest仅限于单个控制器,并与@MockBean结合使用,为所需的协作者提供模拟实现。
@WebMvcTest还自动配置MockMvc。Mock MVC提供了一种强大的方法来快速测试MVC控制器,而不需要启动一个完整的HTTP服务器。
甚至可以在一个非@WebMvcTest(比如@SpringBootTest)中自动配置MockMvc,方法是使用@AutoConfigureMockMvc进行注释。以下是一个使用了MockMvc的例子:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
}
3.11 自动配置的Spring WebFlux测试
要测试Spring WebFlux控制器是否按预期工作,可以使用@WebFluxTest注释。@WebFluxTest自动配置Spring WebFlux基础设施,并将扫描的bean限制为@Controller、@ControllerAdvice、@JsonComponent、Converter、GenericConverter、WebFilter和WebFluxConfigurer。当使用@WebFluxTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。@ enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。
如果需要注册额外的组件,比如Jackson模块,可以通过在测试中使用@import来导入额外的配置类。
通常,@WebFluxTest仅限于单个控制器,并与@MockBean注释结合使用,为所需的协作者提供模拟实现。
@WebFluxTest还自动配置WebTestClient,它提供了一个强大的方法来快速测试WebFlux控制器,而不需要启动一个完整的HTTP服务器。
甚至可以通过使用@AutoConfigureWebTestClient进行注释,在非@WebFluxTest(例如@SpringBootTest)中自动配置WebTestClient。下面的例子展示了一个同时使用@WebFluxTest和WebTestClient的类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.mockito.BDDMockito.given;
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Honda Civic");
}
}
3.12 自动配置的Data JPA测试
可使用@DataJpaTest注释来测试JPA应用程序。默认情况下,它扫描@Entity类并配置Spring Data JPA存储库。如果类路径上有一个嵌入式数据库,它也会配置一个。默认情况下,通过将spring.jpa.show-sql属性设置为true来记录SQL查询。可以使用注释的showSql()属性禁用这一功能。
当使用@DataJpaTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。@ enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。
默认情况下,JPA测试是事务性的,并在每个测试结束时回滚。有关更多细节,可参阅Spring框架参考文档中的相关章节。如果并不需要事务,你可以禁用测试或整个类的事务管理,如下所示:
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
Data JPA测试还可以注入TestEntityManager bean,该bean提供了标准JPA EntityManager的替代方案,后者是专门为测试设计的。
根据测试需要,也可以使用JdbcTemplate。下面的例子展示了@DataJpaTest注释的使用:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
class MyRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
void testExample() throws Exception {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getEmployeeNumber()).isEqualTo("1234");
}
}
内存中的嵌入式数据库通常可以很好地用于测试,因为速度快且不需要安装。但是,如果更偏向在真实的数据库运行测试,则可以使用@AutoConfigureTestDatabase注释,如下例所示:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
3.13 自动配置的JDBC测试
@JdbcTest和@DataJpaTest类似,但是它用于只需要数据源且不使用Spring Data JDBC的测试情景。默认情况下,它配置一个内存内嵌入式数据库和一个JdbcTemplate。当使用@JdbcTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。@enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。
默认情况下,JDBC测试是事务性的,并在每个测试结束时回滚。有关更多细节,可参阅Spring框架参考文档中的相关章节。如果不需要事务,可以禁用一个测试或整个类的事务管理,如下所示:
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {
}
如果更倾向于运行在真实数据库中,可以参照DataJpaTest中的@AutoConfigureTestDatabase做法。
3.14 自动配置的Data JDBC测试@DataJdbcTest与@JdbcTest类似,但@DataJdbcTest用于使用Spring Data JDBC存储库的测试。默认情况下,它配置内存中的嵌入式数据库、JdbcTemplate和Spring Data JDBC存储库。当使用@DataJdbcTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。@ enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。
默认情况下,Data JDBC测试是事务性的,并在每次测试结束时回滚。有关更多细节,请参阅Spring框架参考文档中的相关章节。如果不需要使用事务,可禁用测试或整个测试类的事务管理,如JDBC示例所示。
如果更倾向于运行在真实数据库中,可以参照DataJpaTest中的@AutoConfigureTestDatabase做法。
可以使用@DataRedisTest来测试Redis应用程序。默认情况下,它扫描@RedisHash类并配置Spring Data Redis存储库。当使用@DataRedisTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。@ enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。(关于在Spring Boot中使用Redis的更多信息,请参见“Redis”章。)
一个使用@DataRedisTest注释的例子:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
3.16 自动配置的REST客户端
可以使用@RestClientTest注释来测试REST客户端。默认情况下,它会自动配置Jackson、GSON和Jsonb支持,配置RestTemplateBuilder,并添加对MockRestServiceServer的支持。当使用@RestClientTest注释时,常规的@Component和@ConfigurationProperties bean不会被扫描。@ enableconconfigurationproperties可以用来包含@ConfigurationProperties bean。
想要测试的特定bean应该通过使用@RestClientTest的value或components属性来指定,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() throws Exception {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
3.17 使用Mock MVC自动配置Spring REST Docs测试
@AutoConfigureRestDocs在测试基于servlet的web应用时,自定义MockMvc bean来使用Spring REST Docs。可以通过使用@Autowired注入它,并在对应test中使用它,就像在使用Mock MVC和Spring REST Docs时通常会做的那样,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestdocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserdocumentationTests {
@Autowired
private MockMvc mvc;
@Test
void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}
}
如果需要对Spring REST Docs配置进行更多的控制,而不是通过@AutoConfigureRestDocs的属性提供的,可以使用RestDocsMockMvcConfigurationCustomizer bean,如下所示:
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.mockmvc.MockMvcRestdocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestdocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
如果想要使用Spring REST Docs对参数化输出目录的支持,可以创建一个RestdocumentationResultHandler bean。自动配置调用总是使用这个结果处理程序的sdo,因此导致每个MockMvc调用自动生成默认的代码段。下面的例子显示了RestdocumentationResultHandler的定义:
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestdocumentation;
import org.springframework.restdocs.mockmvc.RestdocumentationResultHandler;
@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {
@Bean
public RestdocumentationResultHandler restdocumentation() {
return MockMvcRestdocumentation.document("{method-name}");
}
}
3.18 使用WebTestClient自动配置Spring REST Docs测试
@AutoConfigureRestDocs也可以与WebTestClient一起用于测试响应式web应用程序。可以使用@Autowired注入它,并在对应test中使用它,就像使用@WebFluxTest和Spring REST Docs时一样,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestdocumentation.document;
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersdocumentationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void listUsers() {
this.webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(document("list-users"));
}
}
如果需要对Spring REST Docs配置进行更多的控制,而不是通过@AutoConfigureRestDocs的属性提供的,可以使用RestDocsWebTestClientConfigurationCustomizer bean,如下所示:
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.webtestclient.WebTestClientRestdocumentationConfigurer;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {
@Override
public void customize(WebTestClientRestdocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}
}
如果想利用Spring REST Docs对参数化输出目录的支持,可以使用WebTestClientBuilderCustomizer为每个实体交换结果配置一个消费者。下面的例子展示了这样一个WebTestClientBuilderCustomizer正在被定义:
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import static org.springframework.restdocs.webtestclient.WebTestClientRestdocumentation.document;
@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
public WebTestClientBuilderCustomizer restdocumentation() {
return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
}
}
3.19 使用REST Assured自动配置Spring REST文档测试
@AutoConfigureRestDocs使RequestSpecification bean可以在测试中使用,该bean预先配置为使用Spring REST Docs。可以通过使用@Autowired来注入它,并在对应test中使用它,就像使用REST Assured和Spring REST Docs时通常会使用它一样,如下面的例子所示:
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.springframework.restdocs.restassured3.RestAssuredRestdocumentation.document;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserdocumentationTests {
@Test
void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
given(documentationSpec)
.filter(document("list-users"))
.when()
.port(port)
.get("/")
.then().assertThat()
.statusCode(is(200));
}
}
如果需要对Spring REST Docs配置进行更多的控制,而不是通过@AutoConfigureRestDocs的属性提供的,可以使用RestDocsRestAssuredConfigurationCustomizer bean,如下所示:
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured3.RestAssuredRestdocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestdocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
3.20 自动配置的Spring Web服务 客户端测试
可以使用@WebServiceClientTest来测试使用Spring web services项目调用web服务的应用程序。默认情况下,它会配置一个模拟WebServiceServer bean,并自动定制WebServiceTemplateBuilder。(关于在Spring Boot中使用Web服务的更多信息,请参见的“Web服务”。)
一个使用@WebServiceClientTest的例子:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.xml.transform.StringSource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;
@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {
@Autowired
private MockWebServiceServer server;
@Autowired
private SomeWebService someWebService;
@Test
void mockServerCall() {
this.server
.expect(payload(new StringSource(" ")))
.andRespond(withPayload(new StringSource("200 ")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}
}
3.21 自动配置的Spring Web服务 服务端测试
可以使用@WebServiceServerTest来测试使用Spring web services项目实现web服务的应用程序。默认情况下,它配置了一个MockWebServiceClient bean,可以用来调用web服务端点。(关于在Spring Boot中使用Web服务的更多信息,请参见“Web服务”。)
一个使用@WebServiceServerTest的例子:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest;
import org.springframework.ws.test.server.MockWebServiceClient;
import org.springframework.ws.test.server.RequestCreators;
import org.springframework.ws.test.server.ResponseMatchers;
import org.springframework.xml.transform.StringSource;
@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {
@Autowired
private MockWebServiceClient client;
@Test
void mockServerCall() {
this.client
.sendRequest(RequestCreators.withPayload(new StringSource(" ")))
.andExpect(ResponseMatchers.payload(new StringSource("42 ")));
}
}
3.22 额外的自动配置及切片
每个切片提供一个或多个@AutoConfigure…注解,即定义的自动配置应该包含在一个切片中。额外的自动配置可以通过创建一个自定义的@AutoConfigure…注释或添加@importAutoConfiguration到测试中来添加,如下所示:
@JdbcTest
@importAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {
}
确保不要使用常规的@import注释来导入自动配置,因为Spring Boot会以特定的方式处理自动配置。
另外,可以通过在meta-INF/spring.factories中注册切片注释,为切片注释的使用添加额外的自动配置,示例所示:
org.springframework.boot.test.autoconfigure.jdbc.JdbcTest=com.example.IntegrationAutoConfiguration4. 测试工具
在测试应用程序时,spring-boot中有几个很有用的测试实用程序类,他们被打包为spring-boot的一部分。
4.1 ConfigDataApplicationContextInitializerConfigDataApplicationContextInitializer是一个ApplicationContextInitializer,可以将它应用到测试中来加载Spring Boot的属性文件:application.properties。当不需要@SpringBootTest提供的全部特性集时,可以使用它,如下所示:
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}
只使用ConfigDataApplicationContextInitializer,不支持@Value("${…}")注入。它唯一的作用是确保application.properties文件能被加载到Spring环境中。对于@Value支持,则需要另外配置一个propertysourcesconfigururer或者使用@SpringBootTest,它会自动配置。
4.2 TestPropertyValuesTestPropertyValues允许快速向ConfigurableEnvironment或ConfigurableApplicationContext添加属性。可以用key=value字符串调用它,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
class MyEnvironmentTests {
@Test
void testPropertySources() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
assertThat(environment.getProperty("name")).isEqualTo("Boot");
}
}
4.3 OutputCapture
OutputCapture是一个JUnit扩展,可以使用它来捕获System.out和System.err的输出。要使用@ExtendWith(OutputCaptureExtension.class),并将CapturedOutput作为参数注入到测试类中的构造函数或测试方法,如下所示:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
4.4 TestRestTemplate
TestRestTemplate是Spring中RestTemplate的便捷替代品,在集成测试中非常有用。使用它,可以获得普通模板或发送基本HTTP身份验证(带有用户名和密码)的模板。以上两种情况下,模板都是具有容错性质的。这意味着它以一种测试友好的方式运行,不会在4xx和5xx错误时抛出异常。相反,这样的错误可以通过返回的ResponseEntity及其状态代码来检测。
PS:Spring framework 5.0提供了一个新的WebTestClient,它适用于WebFlux的集成测试以及WebFlux和MVC的端到端测试。与TestRestTemplate不同,它为断言提供了流畅的API。
建议(但不是强制)使用Apache HTTP客户端(版本4.3.2或更好)。如果类路径中正好有它,那么TestRestTemplate可通过适当地配置客户端来响应。如果使用Apache的HTTP客户端,会启用一些额外的测试友好特性:
-
不支持重定向(因此可以断言响应位置)。
-
cookie被忽略(因此模板是无状态的)。
TestRestTemplate可以在集成测试中直接实例化,如下例所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
class MyTests {
private TestRestTemplate template = new TestRestTemplate();
@Test
void testRequest() throws Exception {
ResponseEntity headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}
}
或者,如果在WebEnvironment中使用@SpringBootTest注释WebEnvironment.RANDOM_PORT或WebEnvironment.DEFINED_PORT,可以注入一个完全配置的TestRestTemplate并开始使用它。如果需要,可以通过RestTemplateBuilder bean应用额外的定制。任何不指定主机和端口的url都会自动连接到嵌入式服务器,示例如下:
import java.time.Duration;
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.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration(proxyBeanMethods = false)
static class RestTemplateBuilderConfiguration {
@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}



