测试 Spring Boot 应用程序
一个 Spring Boot 应用程序就是一个 Spring ApplicationContext,因此除了对普通 Spring 上下文通常所做的测试之外,无需进行任何特别的操作来测试它。
外部属性、日志记录以及 Spring Boot 的其他特性默认仅在你使用 SpringApplication 创建上下文时才会被安装到上下文中。
Spring Boot 提供了 @SpringBootTest 注解,当你需要使用 Spring Boot 特性时,它可以作为标准 spring-test @ContextConfiguration 注解的替代方案。该注解通过 SpringApplication 来创建测试中使用的 ApplicationContext。除了 @SpringBootTest 之外,还提供了许多其他注解,用于测试应用程序中更具体的切片。
如果你使用的是 JUnit 4,请不要忘记在测试类上添加 @RunWith(SpringRunner.class),否则这些注解将被忽略。如果你使用的是 JUnit 5,则无需显式添加等效的 @ExtendWith(SpringExtension.class),因为 @SpringBootTest 以及其他 @…Test 注解已经内置了该注解。
默认情况下,@SpringBootTest 不会启动服务器。你可以使用 @SpringBootTest 的 webEnvironment 属性进一步细化测试的运行方式:
-
MOCK(默认):加载一个 Web ApplicationContext 并提供一个模拟的 Web 环境。使用此注解时不会启动内嵌服务器。如果 classpath 中没有可用的 Web 环境,此模式会透明地回退到创建一个普通的非 Web ApplicationContext。它可以与 @AutoConfigureMockMvc 或 @AutoConfigureWebTestClient 结合使用,以基于模拟的方式测试你的 Web 应用程序。 -
RANDOM_PORT:加载一个 WebServerApplicationContext 并提供真实的 Web 环境。内嵌服务器会被启动,并监听一个随机端口。 -
DEFINED_PORT:加载一个 WebServerApplicationContext 并提供真实的 Web 环境。内嵌服务器会被启动,并监听一个指定端口(来自你的application.properties文件)或默认端口8080。 -
NONE:通过 SpringApplication 加载一个 ApplicationContext,但不提供 任何 Web 环境(无论是模拟的还是其他形式的)。
如果你的测试使用了 @Transactional,默认情况下会在每个测试方法结束时回滚事务。然而,当与 RANDOM_PORT 或 DEFINED_PORT 一起使用时,这种配置会隐式地提供一个真实的 Servlet 环境,导致 HTTP 客户端和服务器运行在不同的线程中,因此也处于不同的事务中。在这种情况下,服务器上发起的任何事务都不会被回滚。
如果您的应用程序为管理服务器(management server)使用了不同的端口,那么带有 webEnvironment = WebEnvironment.RANDOM_PORT 的 @SpringBootTest 也会在另一个随机端口上启动管理服务器。
检测 Web 应用程序类型
如果 Spring MVC 可用,则会配置一个基于常规 MVC 的应用上下文。如果你只有 Spring WebFlux,我们会检测到这一点,并改用基于 WebFlux 的应用上下文进行配置。
如果两者都存在,Spring MVC 优先。如果要在此场景下测试响应式 Web 应用程序,必须设置 spring.main.web-application-type 属性:
- Java
- Kotlin
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {
// ...
}
检测测试配置
如果你熟悉 Spring Test Framework,可能已经习惯使用 @ContextConfiguration(classes=…) 来指定要加载的 Spring @Configuration 类。或者,你也可能经常在测试类中使用嵌套的 @Configuration 类。
在测试 Spring Boot 应用程序时,通常不需要这样做。当你没有显式定义主配置时,Spring Boot 的 @*Test 注解会自动搜索你的主配置。
搜索算法会从包含测试的包开始向上查找,直到找到一个带有 @SpringBootApplication 或 @SpringBootConfiguration 注解的类。只要你以合理的方式组织了你的代码,通常就能找到主配置。
如果你使用 测试注解来测试应用程序中更具体的切片,应避免在 主方法的应用程序类 上添加特定于某个区域的配置设置。
@SpringBootApplication 底层的组件扫描配置定义了排除过滤器(exclude filters),用于确保切片(slicing)能按预期工作。如果你在带有 @SpringBootApplication 注解的类上显式使用了 @ComponentScan 指令,请注意这些过滤器将被禁用。如果你使用了切片,应重新定义这些过滤器。
如果你想自定义主配置,可以使用一个嵌套的 @TestConfiguration 类。与嵌套的 @Configuration 类(它会替代你应用程序的主配置)不同,嵌套的 @TestConfiguration 类是在你应用程序的主配置之外额外使用的。
Spring 的测试框架会在测试之间缓存应用上下文。因此,只要你的测试共享相同的配置(无论该配置是如何被发现的),加载上下文这一可能耗时的过程就只会发生一次。
使用测试配置的主方法
通常,由 @SpringBootTest 发现的测试配置就是你的主 @SpringBootApplication。在大多数结构良好的应用程序中,该配置类还会包含用于启动应用程序的 main 方法。
例如,以下是一个典型的 Spring Boot 应用程序中非常常见的代码模式:
- Java
- Kotlin
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.docs.using.structuringyourcode.locatingthemainclass.MyApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
在上面的示例中,main 方法除了委托给 SpringApplication.run(Class, String…) 之外没有做任何其他事情。然而,也可以拥有一个更复杂的 main 方法,在调用 SpringApplication.run(Class, String…) 之前应用自定义配置。
例如,以下是一个更改 banner 模式并设置额外配置文件的应用程序:
- Java
- Kotlin
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.setAdditionalProfiles("myprofile");
application.run(args);
}
}
import org.springframework.boot.Banner
import org.springframework.boot.runApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
setAdditionalProfiles("myprofile")
}
}
由于 main 方法中的自定义逻辑可能会影响最终生成的 ApplicationContext,你可能会希望在测试中使用 main 方法来创建所用的 ApplicationContext。默认情况下,@SpringBootTest 不会调用你的 main 方法,而是直接使用该类本身来创建 ApplicationContext。
如果你想更改此行为,可以将 @SpringBootTest 的 useMainMethod 属性设置为 SpringBootTest.UseMainMethod.ALWAYS 或 SpringBootTest.UseMainMethod.WHEN_AVAILABLE。当设置为 ALWAYS 时,如果找不到 main 方法,测试将失败。当设置为 WHEN_AVAILABLE 时,如果存在 main 方法则会使用它,否则将使用标准的加载机制。
例如,以下测试将调用 MyApplication 的 main 方法来创建 ApplicationContext。如果 main 方法设置了额外的 profiles,那么这些 profiles 将在 ApplicationContext 启动时处于激活状态。
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod;
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
void exampleTest() {
// ...
}
}
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
fun exampleTest() {
// ...
}
}
排除测试配置
如果你的应用程序使用了组件扫描(例如,使用了 @SpringBootApplication 或 @ComponentScan),你可能会发现那些仅为特定测试创建的顶层配置类被意外地在所有地方加载。
正如我们之前所见,可以在测试类的内部类上使用 @TestConfiguration 来定制主配置。@TestConfiguration 也可以用于顶层类。这样做表示该类不应被组件扫描自动发现。然后,你可以显式地在需要的地方导入该类,如下例所示:
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import
@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {
@Test
fun exampleTest() {
// ...
}
}
如果你直接使用 @ComponentScan(即不通过 @SpringBootApplication),则需要向其注册 TypeExcludeFilter。详情请参见 TypeExcludeFilter 的 API 文档。
导入的 @TestConfiguration 会比内部类的 @TestConfiguration 更早被处理,而且导入的 @TestConfiguration 也会在通过组件扫描发现的任何配置之前被处理。一般来说,这种顺序上的差异不会产生明显影响,但如果你依赖于 Bean 的覆盖行为,则需要注意这一点。
使用 Application Arguments
如果你的应用程序需要 arguments,你可以使用 @SpringBootTest 的 args 属性来注入它们。
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@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");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {
@Test
fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
assertThat(args.optionNames).containsOnly("app.test")
assertThat(args.getOptionValues("app.test")).containsOnly("one")
}
}
使用 Mock 环境进行测试
默认情况下,@SpringBootTest 不会启动服务器,而是设置一个用于测试 Web 端点的模拟环境。
使用 Spring MVC,我们可以通过 MockMvc 来查询我们的 Web 端点。目前提供了三种集成方式:
-
使用 Hamcrest 的常规 MockMvc。
-
包装了 MockMvc 并使用 AssertJ 的 MockMvcTester。
-
WebTestClient,其中将 MockMvc 作为服务器来处理请求。
以下示例展示了可用的集成:
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import static org.assertj.core.api.Assertions.assertThat;
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;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If AssertJ is on the classpath, you can use MockMvcTester
@Test
void testWithMockMvcTester(@Autowired MockMvcTester mvc) {
assertThat(mvc.get().uri("/")).hasStatusOk().hasBodyTextEqualTo("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");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.assertj.MockMvcTester
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
fun testWithMockMvc(@Autowired mvc: MockMvcTester) {
assertThat(mvc.get().uri("/")).hasStatusOk()
.hasBodyTextEqualTo("Hello World")
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
如果你只想专注于 Web 层,而不启动完整的 ApplicationContext,请考虑使用 @WebMvcTest。
使用 Spring WebFlux 端点时,你可以使用 WebTestClient,如下例所示:
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
在模拟环境中进行测试通常比使用完整的 Servlet 容器运行更快。然而,由于模拟发生在 Spring MVC 层,依赖于底层 Servlet 容器行为的代码无法直接通过 MockMvc 进行测试。
例如,Spring Boot 的错误处理基于 Servlet 容器提供的“错误页面”支持。这意味着,虽然你可以测试你的 MVC 层是否按预期抛出和处理异常,但无法直接测试某个特定的自定义错误页面是否被渲染。如果你需要测试这些底层相关的问题,可以按照下一节所述启动一个完整运行的服务器。
使用运行中的服务器进行测试
如果你需要启动一个完整的运行服务器,我们建议你使用随机端口。如果你使用 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT),每次运行测试时都会随机选择一个可用的端口。
[@LocalServer[port]](https://docs.spring.io/spring-boot/3.5.10/api/java/org/springframework/boot/test/web/server/LocalServerPort.html) 注解可用于 将实际使用的端口注入 到你的测试中。为了方便起见,需要向已启动的服务器发起 REST 调用的测试还可以额外自动装配一个 WebTestClient,它能够将相对链接解析为正在运行的服务器,并提供了专门用于验证响应的 API,如下例所示:
- Java
- Kotlin
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.test.web.reactive.server.WebTestClient;
@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");
}
}
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.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
通过在测试类上添加 @AutoConfigureWebTestClient 注解,WebTestClient 也可以与 mock 环境 一起使用,从而无需启动服务器。
此设置要求 classpath 中包含 spring-webflux。如果你无法或不想添加 webflux,Spring Boot 还提供了 TestRestTemplate 工具:
- Java
- Kotlin
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.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
import org.assertj.core.api.Assertions.assertThat
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.web.client.TestRestTemplate
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
fun exampleTest(@Autowired restTemplate: TestRestTemplate) {
val body = restTemplate.getForObject("/", String::class.java)
assertThat(body).isEqualTo("Hello World")
}
}
自定义 WebTestClient
要自定义 WebTestClient Bean,请配置一个 WebTestClientBuilderCustomizer Bean。所有此类 Bean 都会使用用于创建 WebTestClient 的 WebTestClient.Builder 进行调用。
使用 JMX
由于测试上下文框架会缓存上下文,JMX 默认被禁用,以防止相同的组件注册到同一域中。如果此类测试需要访问 MBeanServer,请考虑也将其标记为 dirty:
- Java
- Kotlin
import javax.management.MBeanServer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
import javax.management.MBeanServer
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext
@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {
@Test
fun exampleTest() {
assertThat(mBeanServer.domains).contains("java.lang")
// ...
}
}
使用 Observations
如果你在 切片测试 上添加 @AutoConfigureObservability 注解,它会自动配置一个 ObservationRegistry。
使用 Metrics
无论你的 classpath 如何,当使用 @SpringBootTest 时,除了基于内存的 meter registries 之外,其他 meter registries 都不会被自动配置。
如果你需要在集成测试中将指标导出到不同的后端,请使用 @AutoConfigureObservability 注解。
如果你使用 @AutoConfigureObservability 注解标注 一个切片测试,它会自动配置一个内存中的 MeterRegistry。在切片测试中,使用 @AutoConfigureObservability 注解不支持数据导出。
使用 Tracing
无论你的 classpath 如何,当使用 @SpringBootTest 时,用于上报数据的 tracing 组件不会被自动配置。
如果你需要将这些组件作为集成测试的一部分,请使用 @AutoConfigureObservability 注解该测试。
如果你已经创建了自己的报告组件(例如自定义的 SpanExporter 或 brave.handler.SpanHandler),并且不希望它们在测试中被激活,可以使用 @ConditionalOnEnabledTracing 注解来禁用它们。
如果你使用 @AutoConfigureObservability 注解标注 一个切片测试,它会自动配置一个空操作(no-op)的 Tracer。在切片测试中,使用 @AutoConfigureObservability 注解不支持数据导出。
Mocking 和 Spying Beans
在运行测试时,有时需要模拟(mock)应用程序上下文中的某些组件。例如,你可能有一个封装了某些远程服务的外观(facade),而该远程服务在开发期间不可用。当你想要模拟一些在真实环境中难以触发的故障时,模拟也非常有用。
Spring Framework 包含一个 @MockitoBean 注解,可用于在你的 ApplicationContext 中定义一个 Mockito mock。此外,@MockitoSpyBean 可用于定义一个 Mockito spy。有关这些特性的更多信息,请参阅 Spring Framework 文档。
自动配置的测试
Spring Boot 的自动配置系统对应用程序运行良好,但有时对测试来说可能显得过于繁重。通常,仅加载测试应用程序某个“切片”(slice)所需的配置部分会更有帮助。例如,你可能想测试 Spring MVC 控制器是否正确映射了 URL,而不希望在这些测试中涉及数据库调用;或者你可能想测试 JPA 实体,而在运行这些测试时并不关心 Web 层。
spring-boot-test-autoconfigure 模块包含多个注解,可用于自动配置此类“切片”。它们的工作方式类似,均提供一个 @…Test 注解用于加载 ApplicationContext,以及一个或多个 @AutoConfigure… 注解,用于自定义自动配置设置。
每个切片将组件扫描限制为适当的组件,并仅加载非常有限的一组自动配置类。如果你需要排除其中某个自动配置类,大多数 @…Test 注解都提供了一个 excludeAutoConfiguration 属性。或者,你也可以使用 @ImportAutoConfiguration#exclude。
不支持通过在一个测试中使用多个 @…Test 注解来包含多个“切片”。如果你需要多个“切片”,请选择其中一个 @…Test 注解,并手动添加其他“切片”对应的 @AutoConfigure… 注解。
也可以将 @AutoConfigure… 注解与标准的 @SpringBootTest 注解一起使用。如果你不打算对应用程序进行“切片”测试,但仍希望使用某些自动配置的测试 Bean,就可以采用这种组合方式。
自动配置的 JSON 测试
-
Jackson ObjectMapper、任何 @JsonComponent Bean 以及任何 Jackson Module
-
Gson -
Jsonb
如果你需要配置自动配置的某些元素,可以使用 @AutoConfigureJsonTesters 注解。
Spring Boot 提供了基于 AssertJ 的辅助工具,可与 JSONAssert 和 JsonPath 库配合使用,用于验证 JSON 是否符合预期。可以分别使用 JacksonTester、GsonTester、JsonbTester 和 BasicJsonTester 类来处理 Jackson、Gson、Jsonb 和字符串。在使用 @JsonTest 时,测试类中的任意辅助字段都可以通过 @Autowired 注入。以下示例展示了一个针对 Jackson 的测试类:
- Java
- Kotlin
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<VehicleDetails> 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");
}
}
import org.assertj.core.api.Assertions.assertThat
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
@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {
@Test
fun serialize() {
val details = VehicleDetails("Honda", "Civic")
// Assert against a `.json` file in the same package as the test
assertThat(json.write(details)).isEqualToJson("expected.json")
// Or use JSON path based assertions
assertThat(json.write(details)).hasJsonPathStringValue("@.make")
assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
}
@Test
fun deserialize() {
val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
assertThat(json.parseObject(content).make).isEqualTo("Ford")
}
}
JSON 辅助类也可以直接在标准单元测试中使用。如果不使用 @JsonTest,则可以在你的 @BeforeEach 方法中调用该辅助类的 initFields 方法。
如果你使用 Spring Boot 基于 AssertJ 的辅助方法来断言某个 JSON 路径下的数值,可能无法根据该数值的类型直接使用 isEqualTo。此时,你可以使用 AssertJ 的 satisfies 来断言该值满足给定的条件。例如,以下示例断言实际数值是一个浮点数,且在 0.01 的偏移范围内接近 0.15。
- Java
- Kotlin
@Test
void someTest() throws Exception {
SomeObject value = new SomeObject(0.152f);
assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
@Test
fun someTest() {
val value = SomeObject(0.152f)
assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies(ThrowingConsumer { number ->
assertThat(number.toFloat()).isCloseTo(0.15f, within(0.01f))
})
}
自动配置的 Spring MVC 测试
要测试 Spring MVC 控制器是否按预期工作,请使用 @WebMvcTest 注解。@WebMvcTest 会自动配置 Spring MVC 基础设施,并将扫描的 Bean 限制为 @Controller、@ControllerAdvice、@JsonComponent、Converter、GenericConverter、Filter、HandlerInterceptor、WebMvcConfigurer、WebMvcRegistrations 和 HandlerMethodArgumentResolver。当使用 @WebMvcTest 注解时,普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @WebMvcTest 启用的自动配置设置列表可以在附录中找到。
通常,@WebMvcTest 仅限于单个控制器,并与 @MockitoBean 结合使用,为所需的协作者提供模拟实现。
@WebMvcTest 还会自动配置 MockMvc。Mock MVC 提供了一种强大而快速的方式,用于测试 MVC 控制器,而无需启动完整的 HTTP 服务器。如果 AssertJ 可用,则还会自动配置由 MockMvcTester 提供的 AssertJ 支持。
你也可以通过使用 @AutoConfigureMockMvc 注解,在非 @WebMvcTest(例如 @SpringBootTest)的测试中自动配置 MockMvc 和 MockMvcTester。以下示例使用了 MockMvcTester:
- Java
- Kotlin
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.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvcTester mvc;
@MockitoBean
private UserVehicleService userVehicleService;
@Test
void testExample() {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
assertThat(this.mvc.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.hasStatusOk()
.hasBodyTextEqualTo("Honda Civic");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.test.web.servlet.assertj.MockMvcTester
@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvcTester) {
@MockitoBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
assertThat(mvc.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.hasStatusOk().hasBodyTextEqualTo("Honda Civic")
}
}
如果你需要配置自动配置的某些元素(例如,指定应在何时应用 Servlet 过滤器),可以使用 @AutoConfigureMockMvc 注解中的属性。
- Java
- Kotlin
import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlPage;
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.test.context.bean.override.mockito.MockitoBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockitoBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.htmlunit.WebClient
import org.htmlunit.html.HtmlPage
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.context.bean.override.mockito.MockitoBean
@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {
@MockitoBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
assertThat(page.body.textContent).isEqualTo("Honda Civic")
}
}
Spring Boot 创建的 webDriver 作用域将替换任何用户定义的同名作用域。如果你定义了自己的 webDriver 作用域,当你使用 @WebMvcTest 时,可能会发现它停止工作。
如果你的 classpath 中包含 Spring Security,@WebMvcTest 也会扫描 WebSecurityConfigurer bean。对于这类测试,与其完全禁用安全机制,不如使用 Spring Security 提供的测试支持。有关如何使用 Spring Security 的 MockMvc 支持的更多详细信息,请参阅 Testing With Spring Security “How-to Guides” 部分。
有时仅编写 Spring MVC 测试是不够的;Spring Boot 可以帮助你运行带有实际服务器的完整端到端测试。
自动配置的 Spring WebFlux 测试
为了测试 Spring WebFlux 控制器是否按预期工作,你可以使用 @WebFluxTest 注解。@WebFluxTest 会自动配置 Spring WebFlux 基础设施,并将扫描的 Bean 限制为 @Controller、@ControllerAdvice、@JsonComponent、Converter、GenericConverter 和 WebFluxConfigurer。当使用 @WebFluxTest 注解时,普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @WebFluxTest 启用的自动配置列表可以在附录中找到。
通常,@WebFluxTest 仅限于单个控制器,并与 @MockitoBean 注解结合使用,以提供所需协作者的模拟实现。
@WebFluxTest 还会自动配置 WebTestClient,它提供了一种强大的方式,无需启动完整的 HTTP 服务器即可快速测试 WebFlux 控制器。
你也可以通过使用 @AutoConfigureWebTestClient 注解,在非 @WebFluxTest(例如 @SpringBootTest)的测试中自动配置 WebTestClient。以下示例展示了一个同时使用 @WebFluxTest 和 WebTestClient 的类:
- Java
- Kotlin
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.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.mockito.BDDMockito.given;
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockitoBean
private UserVehicleService userVehicleService;
@Test
void testExample() {
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");
}
}
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.http.MediaType
import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {
@MockitoBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Honda Civic")
}
}
此设置仅受 WebFlux 应用程序支持,因为目前在模拟的 Web 应用程序中使用 WebTestClient 仅适用于 WebFlux。
@WebFluxTest 无法检测通过函数式 Web 框架注册的路由。若要测试上下文中的 RouterFunction Bean,可考虑使用 @Import 自行导入你的 RouterFunction,或者使用 @SpringBootTest。
@WebFluxTest 无法检测以 @Bean 形式注册的自定义安全配置,其类型为 SecurityWebFilterChain。若要在测试中包含该配置,你需要使用 @Import 导入注册该 Bean 的配置类,或者改用 @SpringBootTest。
有时仅编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助你运行带有实际服务器的完整端到端测试。
自动配置的 Spring GraphQL 测试
Spring GraphQL 提供了一个专用的测试支持模块;你需要将其添加到你的项目中:
<dependencies>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Unless already present in the compile scope -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
dependencies {
testImplementation("org.springframework.graphql:spring-graphql-test")
// Unless already present in the implementation configuration
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
}
该测试模块提供了 GraphQlTester。该测试器在测试中被广泛使用,因此请务必熟悉其用法。有多种 GraphQlTester 变体,Spring Boot 会根据测试类型自动配置相应的变体:
-
ExecutionGraphQlServiceTester 在服务器端执行测试,无需客户端或传输层
-
HttpGraphQlTester 使用客户端连接到服务器进行测试,可选择是否使用真实运行的服务器
Spring Boot 通过 @GraphQlTest 注解帮助你测试你的 Spring GraphQL Controllers。@GraphQlTest 会自动配置 Spring GraphQL 基础设施,而无需涉及任何传输层或服务器。这将扫描的 Bean 限制为 @Controller、RuntimeWiringConfigurer、JsonComponent、Converter、GenericConverter、DataFetcherExceptionResolver、Instrumentation 和 GraphQlSourceBuilderCustomizer。当使用 @GraphQlTest 注解时,普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @GraphQlTest 启用的自动配置列表可以在附录中找到。
通常,@GraphQlTest 仅限于一组控制器,并与 @MockitoBean 注解结合使用,以提供所需协作者的模拟实现。
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.GraphQlTester;
@GraphQlTest(GreetingController.class)
class GreetingControllerTests {
@Autowired
private GraphQlTester graphQlTester;
@Test
void shouldGreetWithSpecificName() {
this.graphQlTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
@Test
void shouldGreetWithDefaultName() {
this.graphQlTester.document("{ greeting } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Spring!");
}
}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest
import org.springframework.graphql.test.tester.GraphQlTester
@GraphQlTest(GreetingController::class)
internal class GreetingControllerTests {
@Autowired
lateinit var graphQlTester: GraphQlTester
@Test
fun shouldGreetWithSpecificName() {
graphQlTester.document("{ greeting(name: \"Alice\") } ").execute().path("greeting").entity(String::class.java)
.isEqualTo("Hello, Alice!")
}
@Test
fun shouldGreetWithDefaultName() {
graphQlTester.document("{ greeting } ").execute().path("greeting").entity(String::class.java)
.isEqualTo("Hello, Spring!")
}
}
@SpringBootTest 测试是完整的集成测试,涉及整个应用程序。当使用随机端口或指定端口时,会配置一个真实服务器,并自动提供一个 HttpGraphQlTester Bean,以便你可以用它来测试你的服务器。当配置了 MOCK 环境时,你也可以通过在测试类上添加 @AutoConfigureHttpGraphQlTester 注解来请求一个 HttpGraphQlTester Bean:
- Java
- Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
void shouldGreetWithSpecificName(@Autowired HttpGraphQlTester graphQlTester) {
HttpGraphQlTester authenticatedTester = graphQlTester.mutate()
.webTestClient((client) -> client.defaultHeaders((headers) -> headers.setBasicAuth("admin", "ilovespring")))
.build();
authenticatedTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.graphql.test.tester.HttpGraphQlTester
import org.springframework.http.HttpHeaders
import org.springframework.test.web.reactive.server.WebTestClient
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
fun shouldGreetWithSpecificName(@Autowired graphQlTester: HttpGraphQlTester) {
val authenticatedTester = graphQlTester.mutate()
.webTestClient { client: WebTestClient.Builder ->
client.defaultHeaders { headers: HttpHeaders ->
headers.setBasicAuth("admin", "ilovespring")
}
}.build()
authenticatedTester.document("{ greeting(name: \"Alice\") } ").execute()
.path("greeting").entity(String::class.java).isEqualTo("Hello, Alice!")
}
}
自动配置的 Data Cassandra 测试
你可以使用 @DataCassandraTest 来测试 Cassandra 应用程序。默认情况下,它会配置一个 CassandraTemplate,扫描 @Table 注解的类,并配置 Spring Data Cassandra 的仓库。当使用 @DataCassandraTest 注解时,不会扫描普通的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Cassandra 的更多信息,请参阅 Cassandra。)
由 @DataCassandraTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了在 Spring Boot 中使用 Cassandra 测试的典型设置:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest
@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)
自动配置的 Data Couchbase 测试
你可以使用 @DataCouchbaseTest 来测试 Couchbase 应用程序。默认情况下,它会配置一个 CouchbaseTemplate 或 ReactiveCouchbaseTemplate,扫描 @Document 类,并配置 Spring Data Couchbase 仓库。当使用 @DataCouchbaseTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Couchbase 的更多信息,请参见本章前面的 Couchbase 部分。)
由 @DataCouchbaseTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了在 Spring Boot 中使用 Couchbase 测试的典型设置:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;
@DataCouchbaseTest
class MyDataCouchbaseTests {
@Autowired
private SomeRepository repository;
// ...
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest
@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {
// ...
}
自动配置的 Data Elasticsearch 测试
你可以使用 @DataElasticsearchTest 来测试 Elasticsearch 应用程序。默认情况下,它会配置一个 ElasticsearchTemplate,扫描 @Document 类,并配置 Spring Data Elasticsearch 仓库。当使用 @DataElasticsearchTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Elasticsearch 的更多信息,请参见本章前面的 Elasticsearch 部分。)
由 @DataElasticsearchTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;
@DataElasticsearchTest
class MyDataElasticsearchTests {
@Autowired
private SomeRepository repository;
// ...
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest
@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {
// ...
}
自动配置的 Data JPA 测试
你可以使用 @DataJpaTest 注解来测试 JPA 应用程序。默认情况下,它会扫描 @Entity 类并配置 Spring Data JPA 仓库。如果类路径中存在嵌入式数据库,它也会一并进行配置。通过将 spring.jpa.show-sql 属性设置为 true,SQL 查询默认会被记录。这可以通过注解的 showSql 属性禁用。
使用 @DataJpaTest 注解时,普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @DataJpaTest 启用的自动配置设置列表可以在附录中找到。
默认情况下,Data JPA 测试是事务性的,并且在每个测试结束时回滚。更多详细信息,请参阅 Spring Framework 参考文档中的相关章节。如果你不希望如此,可以按如下方式为某个测试或整个类禁用事务管理:
- Java
- Kotlin
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
Data JPA 测试还可以注入一个 TestEntityManager Bean,它提供了一种替代标准 JPA EntityManager 的方式,专为测试而设计。
通过添加 @AutoConfigureTestEntityManager,TestEntityManager 也可以被自动配置到任意基于 Spring 的测试类中。这样做时,请确保你的测试运行在事务中,例如在测试类或方法上添加 @Transactional。
如果你需要的话,也可以使用 JdbcTemplate。以下示例展示了 @DataJpaTest 注解的用法:
- Java
- Kotlin
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() {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getEmployeeNumber()).isEqualTo("1234");
}
}
import org.assertj.core.api.Assertions.assertThat
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
@DataJpaTest
class MyRepositoryTests(@Autowired val entityManager: TestEntityManager, @Autowired val repository: UserRepository) {
@Test
fun testExample() {
entityManager.persist(User("sboot", "1234"))
val user = repository.findByUsername("sboot")
assertThat(user?.username).isEqualTo("sboot")
assertThat(user?.employeeNumber).isEqualTo("1234")
}
}
内存嵌入式数据库通常非常适合用于测试,因为它们速度快且无需任何安装。然而,如果你更倾向于针对真实数据库运行测试,可以使用 @AutoConfigureTestDatabase 注解,如下例所示:
- Java
- Kotlin
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MyRepositoryTests {
// ...
}
自动配置的 JDBC 测试
@JdbcTest 与 @DataJpaTest 类似,但适用于仅需要 DataSource 且不使用 Spring Data JDBC 的测试。默认情况下,它会配置一个内存嵌入式数据库和一个 JdbcTemplate。当使用 @JdbcTest 注解时,默认不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
默认情况下,JDBC 测试是事务性的,并在每个测试结束时回滚。更多详细信息,请参阅 Spring Framework 参考文档中的相关章节。如果你不希望如此,可以为某个测试或整个类禁用事务管理,如下所示:
- Java
- Kotlin
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 {
}
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 注解。(参见 自动配置的 Data JPA 测试。)
自动配置的 Data JDBC 测试
@DataJdbcTest 与 @JdbcTest 类似,但用于测试使用 Spring Data JDBC 仓库的场景。默认情况下,它会配置一个内存嵌入式数据库、一个 JdbcTemplate 以及 Spring Data JDBC 仓库。当使用 @DataJdbcTest 注解时,仅会扫描 AbstractJdbcConfiguration 的子类,而普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @DataJdbcTest 启用的自动配置列表可以在附录中找到。
默认情况下,Data JDBC 测试是事务性的,并在每个测试结束时回滚。更多详细信息,请参阅 Spring Framework 参考文档中的相关章节。如果这不是你想要的行为,你可以为某个测试或整个测试类禁用事务管理,如 JDBC 示例中所示。
如果你希望测试针对真实的数据库运行,可以像使用 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(参见 自动配置的 Data JPA 测试。)
自动配置的 Data R2DBC 测试
@DataR2dbcTest 与 @DataJdbcTest 类似,但用于测试使用 Spring Data R2DBC 仓库的场景。默认情况下,它会配置一个内存中的嵌入式数据库、一个 R2dbcEntityTemplate 以及 Spring Data R2DBC 仓库。当使用 @DataR2dbcTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @DataR2dbcTest 启用的自动配置列表可在附录中找到。
默认情况下,Data R2DBC 测试不是事务性的。
如果你希望测试针对真实数据库运行,可以像使用 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(参见 自动配置的 Data JPA 测试。)
自动配置的 jOOQ 测试
你可以像使用 @JdbcTest 一样使用 @JooqTest,但它是用于 jOOQ 相关的测试。由于 jOOQ 严重依赖于与数据库模式对应的基于 Java 的模式,因此会使用现有的 DataSource。如果你想将其替换为内存数据库,可以使用 @AutoConfigureTestDatabase 来覆盖这些设置。(有关在 Spring Boot 中使用 jOOQ 的更多信息,请参阅 Using jOOQ。)当使用 @JooqTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
@JooqTest 会配置一个 DSLContext。以下示例展示了 @JooqTest 注解的用法:
- Java
- Kotlin
import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
@JooqTest
class MyJooqTests {
@Autowired
private DSLContext dslContext;
// ...
}
import org.jooq.DSLContext
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jooq.JooqTest
@JooqTest
class MyJooqTests(@Autowired val dslContext: DSLContext) {
// ...
}
JOOQ 测试默认是事务性的,并且在每个测试结束时自动回滚。如果你不希望如此,可以为单个测试或整个测试类禁用事务管理,具体方法如 JDBC 示例中所示。
自动配置的 Data MongoDB 测试
你可以使用 @DataMongoTest 来测试 MongoDB 应用程序。默认情况下,它会配置一个 MongoTemplate,扫描 @Document 类,并配置 Spring Data MongoDB 仓库。当使用 @DataMongoTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 MongoDB 的更多信息,请参阅 MongoDB。)
由 @DataMongoTest 启用的自动配置设置列表可以在附录中找到。
以下类展示了 @DataMongoTest 注解的使用:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;
@DataMongoTest
class MyDataMongoDbTests {
@Autowired
private MongoTemplate mongoTemplate;
// ...
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest
import org.springframework.data.mongodb.core.MongoTemplate
@DataMongoTest
class MyDataMongoDbTests(@Autowired val mongoTemplate: MongoTemplate) {
// ...
}
自动配置的 Data Neo4j 测试
你可以使用 @DataNeo4jTest 来测试 Neo4j 应用程序。默认情况下,它会扫描 @Node 类,并配置 Spring Data Neo4j 的 Repository。当使用 @DataNeo4jTest 注解时,普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Neo4j 的更多信息,请参见 Neo4j。)
由 @DataNeo4jTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了在 Spring Boot 中使用 Neo4J 测试的典型设置:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
@DataNeo4jTest
class MyDataNeo4jTests {
@Autowired
private SomeRepository repository;
// ...
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest
@DataNeo4jTest
class MyDataNeo4jTests(@Autowired val repository: SomeRepository) {
// ...
}
默认情况下,Data Neo4j 测试是事务性的,并在每个测试结束时回滚。更多详细信息,请参阅 Spring Framework 参考文档中的相关章节。如果这不是你想要的行为,你可以为某个测试或整个类禁用事务管理,如下所示:
- Java
- Kotlin
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {
}
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests
事务性测试不支持响应式访问。如果你使用这种风格,必须按照上述方式配置 @DataNeo4jTest 测试。
自动配置的 Data Redis 测试
你可以使用 @DataRedisTest 来测试 Redis 应用程序。默认情况下,它会扫描带有 @RedisHash 注解的类,并配置 Spring Data Redis 仓库。当使用 @DataRedisTest 注解时,普通的 @Component 和 @ConfigurationProperties Bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Redis 的更多信息,请参阅 Redis。)
由 @DataRedisTest 启用的自动配置设置列表,可在附录中找到。
以下示例展示了 @DataRedisTest 注解的使用:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest
@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {
// ...
}
自动配置的 Data LDAP 测试
你可以使用 @DataLdapTest 来测试 LDAP 应用程序。默认情况下,它会配置一个内存中的嵌入式 LDAP(如果可用),配置一个 LdapTemplate,扫描 @Entry 类,并配置 Spring Data LDAP 仓库。当使用 @DataLdapTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 LDAP 的更多信息,请参见 LDAP。)
由 @DataLdapTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了 @DataLdapTest 注解的使用:
- Java
- Kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;
@DataLdapTest
class MyDataLdapTests {
@Autowired
private LdapTemplate ldapTemplate;
// ...
}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest
import org.springframework.ldap.core.LdapTemplate
@DataLdapTest
class MyDataLdapTests(@Autowired val ldapTemplate: LdapTemplate) {
// ...
}
内存中的嵌入式 LDAP 通常适用于测试,因为它速度快且无需开发者进行任何安装。然而,如果你更倾向于针对真实的 LDAP 服务器运行测试,则应排除嵌入式 LDAP 的自动配置,如下例所示:
- Java
- Kotlin
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {
// ...
}
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest
@DataLdapTest(excludeAutoConfiguration = [EmbeddedLdapAutoConfiguration::class])
class MyDataLdapTests {
// ...
}
自动配置的 REST 客户端
你可以使用 @RestClientTest 注解来测试 REST 客户端。默认情况下,它会自动配置 Jackson、GSON 和 Jsonb 支持,配置一个 RestTemplateBuilder 和一个 RestClient.Builder,并添加对 MockRestServiceServer 的支持。当使用 @RestClientTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties Bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties Bean。
由 @RestClientTest 启用的自动配置设置列表可在附录中找到。
应使用 @RestClientTest 的 value 或 components 属性来指定你想要测试的特定 Bean。
在测试中的 Bean 里使用 RestTemplateBuilder,并且在构建 RestTemplate 时调用了 RestTemplateBuilder.rootUri(String rootUri),那么在 MockRestServiceServer 的期望中应省略根 URI,如下例所示:
- Java
- Kotlin
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(org.springframework.boot.docs.testing.springbootapplications.autoconfiguredrestclient.RemoteVehicleDetailsService.class)
class MyRestTemplateServiceTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
import org.assertj.core.api.Assertions.assertThat
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 org.springframework.test.web.client.match.MockRestRequestMatchers
import org.springframework.test.web.client.response.MockRestResponseCreators
@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestTemplateServiceTests(
@Autowired val service: RemoteVehicleDetailsService,
@Autowired val server: MockRestServiceServer) {
@Test
fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
server.expect(MockRestRequestMatchers.requestTo("/greet/details"))
.andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
val greeting = service.callRestService()
assertThat(greeting).isEqualTo("hello")
}
}
在测试中的 Bean 里使用 RestClient.Builder,或者使用 RestTemplateBuilder 但未调用 rootUri(String rootURI) 时,必须在 MockRestServiceServer 的期望中使用完整的 URI,如下例所示:
- Java
- Kotlin
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 MyRestClientServiceTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("https://example.com/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
import org.assertj.core.api.Assertions.assertThat
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 org.springframework.test.web.client.match.MockRestRequestMatchers
import org.springframework.test.web.client.response.MockRestResponseCreators
@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestClientServiceTests(
@Autowired val service: RemoteVehicleDetailsService,
@Autowired val server: MockRestServiceServer) {
@Test
fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
server.expect(MockRestRequestMatchers.requestTo("https://example.com/greet/details"))
.andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
val greeting = service.callRestService()
assertThat(greeting).isEqualTo("hello")
}
}
自动配置的 Spring REST Docs 测试
你可以使用 @AutoConfigureRestDocs 注解在你的测试中结合 Mock MVC、REST Assured 或 WebTestClient 使用 Spring REST Docs。它消除了在 Spring REST Docs 中使用 JUnit 扩展的必要性。
@AutoConfigureRestDocs 可用于覆盖默认的输出目录(如果你使用 Maven,则为 target/generated-snippets;如果使用 Gradle,则为 build/generated-snippets)。它还可用于配置出现在任何已记录 URI 中的主机、协议(scheme)和端口。
使用 Mock MVC 的自动配置 Spring REST Docs 测试
@AutoConfigureRestDocs 用于在测试基于 Servlet 的 Web 应用程序时,定制 MockMvc Bean 以使用 Spring REST Docs。你可以通过 @Autowired 注入它,并像平常使用 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.assertj.MockMvcTester;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Autowired
private MockMvcTester mvc;
@Test
void listUsers() {
assertThat(this.mvc.get().uri("/users").accept(MediaType.TEXT_PLAIN)).hasStatusOk()
.apply(document("list-users"));
}
}
如果你更倾向于使用 AssertJ 集成,也可以使用 MockMvcTester,如下例所示:
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.assertj.MockMvcTester;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Autowired
private MockMvcTester mvc;
@Test
void listUsers() {
assertThat(this.mvc.get().uri("/users").accept(MediaType.TEXT_PLAIN)).hasStatusOk()
.apply(document("list-users"));
}
}
两者在底层都复用了同一个 MockMvc 实例,因此对其所做的任何配置都会同时应用于两者。
如果你需要比 @AutoConfigureRestDocs 注解属性所提供的更精细的 Spring REST Docs 配置控制,可以使用 RestDocsMockMvcConfigurationCustomizer bean,如下例所示:
- Java
- Kotlin
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());
}
}
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)
class MyRestDocsConfiguration : RestDocsMockMvcConfigurationCustomizer {
override fun customize(configurer: MockMvcRestDocumentationConfigurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
}
}
如果你想使用 Spring REST Docs 对参数化输出目录的支持,可以创建一个 RestDocumentationResultHandler Bean。自动配置会使用该结果处理器调用 alwaysDo,从而使得每次 MockMvc 调用都会自动生成默认的代码片段(snippets)。以下示例展示了如何定义一个 RestDocumentationResultHandler:
- Java
- Kotlin
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}");
}
}
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)
class MyResultHandlerConfiguration {
@Bean
fun restDocumentation(): RestDocumentationResultHandler {
return MockMvcRestDocumentation.document("{method-name}")
}
}
使用 WebTestClient 自动配置的 Spring REST Docs 测试
@AutoConfigureRestDocs 也可以在测试响应式 Web 应用程序时与 WebTestClient 一起使用。你可以通过 @Autowired 注入它,并像在使用 @WebFluxTest 和 Spring REST Docs 时一样在测试中使用它,如下例所示:
- Java
- Kotlin
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"));
}
}
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.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests(@Autowired val webTestClient: WebTestClient) {
@Test
fun listUsers() {
webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk
.expectBody()
.consumeWith(WebTestClientRestDocumentation.document("list-users"))
}
}
如果你需要比 @AutoConfigureRestDocs 注解属性所提供的更精细的 Spring REST Docs 配置控制,可以使用 RestDocsWebTestClientConfigurationCustomizer Bean,如下例所示:
- Java
- Kotlin
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");
}
}
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsWebTestClientConfigurationCustomizer {
override fun customize(configurer: WebTestClientRestDocumentationConfigurer) {
configurer.snippets().withEncoding("UTF-8")
}
}
如果你想利用 Spring REST Docs 对参数化输出目录的支持,可以使用 WebTestClientBuilderCustomizer 为每个实体交换结果配置一个消费者。以下示例展示了这样一个 WebTestClientBuilderCustomizer 的定义:
- Java
- Kotlin
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}"));
}
}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
import org.springframework.test.web.reactive.server.WebTestClient
@TestConfiguration(proxyBeanMethods = false)
class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
fun restDocumentation(): WebTestClientBuilderCustomizer {
return WebTestClientBuilderCustomizer { builder: WebTestClient.Builder ->
builder.entityExchangeResultConsumer(
WebTestClientRestDocumentation.document("{method-name}")
)
}
}
}
使用 REST Assured 自动配置的 Spring REST Docs 测试
@AutoConfigureRestDocs 会创建一个预先配置好以使用 Spring REST Docs 的 RequestSpecification Bean,并将其提供给你的测试。你可以通过 @Autowired 注入该 Bean,并像平常使用 REST Assured 和 Spring REST Docs 一样在测试中使用它,如下例所示:
- Java
- Kotlin
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.test.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.springframework.restdocs.restassured.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));
}
}
import io.restassured.RestAssured
import io.restassured.specification.RequestSpecification
import org.hamcrest.Matchers
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.test.web.server.LocalServerPort
import org.springframework.restdocs.restassured.RestAssuredRestDocumentation
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Test
fun listUsers(@Autowired documentationSpec: RequestSpecification?, @LocalServerPort port: Int) {
RestAssured.given(documentationSpec)
.filter(RestAssuredRestDocumentation.document("list-users"))
.`when`()
.port(port)["/"]
.then().assertThat()
.statusCode(Matchers.`is`(200))
}
}
如果你需要比 @AutoConfigureRestDocs 注解属性所提供的更精细的 Spring REST Docs 配置控制,可以使用 RestDocsRestAssuredConfigurationCustomizer Bean,如下例所示:
- Java
- Kotlin
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured.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());
}
}
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer
import org.springframework.restdocs.templates.TemplateFormats
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer {
override fun customize(configurer: RestAssuredRestDocumentationConfigurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
}
}
自动配置的 Spring Web Services 测试
自动配置的 Spring Web Services 客户端测试
你可以使用 @WebServiceClientTest 来测试使用 Spring Web Services 项目调用 Web 服务的应用程序。默认情况下,它会配置一个 MockWebServiceServer Bean,并自动定制你的 WebServiceTemplateBuilder。(有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅 Web Services。)
由 @WebServiceClientTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了 @WebServiceClientTest 注解的用法:
- Java
- Kotlin
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("<request/>")))
.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}
}
import org.assertj.core.api.Assertions.assertThat
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.ws.test.client.RequestMatchers
import org.springframework.ws.test.client.ResponseCreators
import org.springframework.xml.transform.StringSource
@WebServiceClientTest(SomeWebService::class)
class MyWebServiceClientTests(
@Autowired val server: MockWebServiceServer, @Autowired val someWebService: SomeWebService) {
@Test
fun mockServerCall() {
server
.expect(RequestMatchers.payload(StringSource("<request/>")))
.andRespond(ResponseCreators.withPayload(StringSource("<response><status>200</status></response>")))
assertThat(this.someWebService.test()).extracting(Response::status).isEqualTo(200)
}
}
自动配置的 Spring Web Services 服务器测试
你可以使用 @WebServiceServerTest 来测试使用 Spring Web Services 项目实现 Web 服务的应用程序。默认情况下,它会配置一个 MockWebServiceClient Bean,可用于调用你的 Web 服务端点。(有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅 Web Services。)
由 @WebServiceServerTest 启用的自动配置设置列表可以在附录中找到。
以下示例展示了 @WebServiceServerTest 注解的使用:
- Java
- Kotlin
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("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(new StringSource("<ExampleResponse>42</ExampleResponse>")));
}
}
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 val client: MockWebServiceClient) {
@Test
fun mockServerCall() {
client
.sendRequest(RequestCreators.withPayload(StringSource("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(StringSource("<ExampleResponse>42</ExampleResponse>")))
}
}
附加的自动配置与切片
每个切片提供一个或多个 @AutoConfigure… 注解,这些注解明确指定了应作为切片一部分包含的自动配置。可以通过创建自定义的 @AutoConfigure… 注解,或在测试中添加 @ImportAutoConfiguration,按需为每个测试添加额外的自动配置,如下例所示:
- Java
- Kotlin
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {
}
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration::class)
class MyJdbcTests
确保不要使用常规的 @Import 注解来导入自动配置类,因为 Spring Boot 会以特定的方式处理它们。
或者,可以通过在 META-INF/spring 目录下创建一个文件来注册额外的自动配置,从而为任何使用 slice 注解的情况添加这些配置,如下例所示:
com.example.IntegrationAutoConfiguration
在此示例中,所有使用 @JdbcTest 注解的测试都会启用 com.example.IntegrationAutoConfiguration。
你可以在该文件中使用 # 添加注释。
只要一个切片(slice)或 @AutoConfigure… 注解使用 @ImportAutoConfiguration 进行元注解(meta-annotated),就可以通过这种方式进行自定义。
用户配置与切片
因此,避免在应用程序的主类中混杂特定于某项功能的配置设置就变得尤为重要。
假设你正在使用 Spring Data MongoDB,依赖其自动配置,并且已启用了审计功能。你可以将你的 @SpringBootApplication 定义如下:
- Java
- Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {
// ...
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.data.mongodb.config.EnableMongoAuditing
@SpringBootApplication
@EnableMongoAuditing
class MyApplication {
// ...
}
由于该类是测试的源配置,任何切片测试实际上都会尝试启用 Mongo 审计,这显然不是你想要的行为。推荐的做法是将该特定区域的配置移动到与你的应用程序同级的一个独立的 @Configuration 类中,如下例所示:
- Java
- Kotlin
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {
// ...
}
import org.springframework.context.annotation.Configuration
import org.springframework.data.mongodb.config.EnableMongoAuditing
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
class MyMongoConfiguration {
// ...
}
根据应用程序的复杂性,你可以为自定义配置使用单个 @Configuration 类,也可以为每个领域区域分别使用一个类。后一种方法允许你在必要时通过 @Import 注解在某个测试中启用特定的配置类。有关何时需要为切片测试(slice tests)启用特定 @Configuration 类的更多详细信息,请参阅 此操作指南章节。
测试切片(Test slices)会从扫描中排除 @Configuration 类。例如,对于一个 @WebMvcTest,以下配置将不会把指定的 WebMvcConfigurer Bean 包含在测试切片所加载的应用上下文中:
- Java
- Kotlin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {
@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
// ...
};
}
}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
class MyWebConfiguration {
@Bean
fun testConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
// ...
}
}
}
然而,下面的配置会导致自定义的 WebMvcConfigurer 被测试切片加载。
- Java
- Kotlin
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
// ...
}
import org.springframework.stereotype.Component
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Component
class MyWebMvcConfigurer : WebMvcConfigurer {
// ...
}
另一个容易引起混淆的地方是类路径扫描(classpath scanning)。假设虽然你已经以合理的方式组织了代码,但仍需要扫描一个额外的包。你的应用程序可能类似于以下代码:
- Java
- Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.ComponentScan
@SpringBootApplication
@ComponentScan("com.example.app", "com.example.another")
class MyApplication {
// ...
}
这样做实际上会覆盖默认的组件扫描指令,并带来一个副作用:无论你选择了哪个切片(slice),都会扫描那两个包。例如,一个 @DataJpaTest 似乎会突然扫描你的应用程序中的组件和用户配置。同样地,将自定义指令移到一个单独的类中是解决此问题的好方法。
如果这对你来说不是一个可行的选项,你可以在测试类的某个层级中创建一个 @SpringBootConfiguration,以替代默认行为。或者,你也可以为测试指定一个源(source),这将禁用自动查找默认配置的行为。
使用 Spock 测试 Spring Boot 应用程序
Spock 2.2 或更高版本可用于测试 Spring Boot 应用程序。为此,请在应用程序的构建中添加一个 -groovy-4.0 版本的 Spock spock-spring 模块依赖项。spock-spring 将 Spring 的测试框架集成到 Spock 中。更多详细信息,请参阅 Spock Spring 模块的文档。