测试 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 不会启动服务器。您可以使用 @SpringBootTestwebEnvironment 属性来进一步细化测试的运行方式。

  • MOCK(默认):加载一个 web ApplicationContext 并提供一个模拟的 web 环境。使用此注解时不会启动嵌入式服务器。如果您的类路径上没有 web 环境,此模式会透明地回退到创建一个普通的非 web ApplicationContext。它可以与 @AutoConfigureMockMvc@AutoConfigureWebTestClient 结合使用,以对您的 web 应用程序进行基于模拟的测试。

  • RANDOM_PORT:加载一个 WebServerApplicationContext 并提供一个真实的 web 环境。嵌入式服务器会启动并在随机端口上监听。

  • DEFINED_PORT:加载一个 WebServerApplicationContext 并提供一个真实的 web 环境。嵌入式服务器会启动并在定义的端口(来自您的 application.properties)或默认端口 8080 上监听。

  • NONE:使用 SpringApplication 加载一个 ApplicationContext,但不提供任何 web 环境(模拟或其他)。

如果您的测试是 @Transactional,它默认会在每个测试方法结束时回滚事务。但是,由于使用此安排与 RANDOM_PORTDEFINED_PORT 隐式地提供了一个真实的 servlet 环境,因此 HTTP 客户端和服务器在不同的线程中运行,因此在不同的事务中运行。在这种情况下,在服务器上启动的任何事务都不会回滚。
@SpringBootTestwebEnvironment = WebEnvironment.RANDOM_PORT 结合使用,如果您的应用程序使用不同的端口启动管理服务器,则也会在另一个随机端口启动管理服务器。

检测 Web 应用程序类型

如果 Spring MVC 可用,则会配置一个基于 MVC 的常规应用程序上下文。如果您只有 Spring WebFlux,我们将检测到它并配置一个基于 WebFlux 的应用程序上下文。

如果两者都存在,则 Spring MVC 优先。如果您想在此场景中测试一个响应式 Web 应用程序,则必须设置 spring.main.web-application-type 属性。

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {

	// ...

}

检测测试配置

如果您熟悉 Spring 测试框架,您可能习惯于使用 @ContextConfiguration(classes=…​) 来指定要加载的 Spring @Configuration。或者,您可能经常在测试中使用嵌套的 @Configuration 类。

在测试 Spring Boot 应用程序时,这通常不需要。Spring Boot 的 @*Test 注释会在您没有明确定义配置时自动搜索您的主配置。

搜索算法从包含测试的包向上搜索,直到找到一个用 @SpringBootApplication@SpringBootConfiguration 注释的类。只要您以合理的方式组织代码,通常会找到您的主配置。

如果您使用测试注释来测试应用程序的更具体的部分,则应避免添加特定于主方法的应用程序类的特定区域的配置设置。

@SpringBootApplication 的底层组件扫描配置定义了排除过滤器,这些过滤器用于确保切片按预期工作。如果您在 @SpringBootApplication 注释的类上使用显式 @ComponentScan 指令,请注意这些过滤器将被禁用。如果您使用切片,则应重新定义它们。

如果您想自定义主配置,可以使用嵌套的 @TestConfiguration 类。与嵌套的 @Configuration 类不同(它将用作应用程序主配置的替代),嵌套的 @TestConfiguration 类将用作应用程序主配置的补充。

Spring 的测试框架在测试之间缓存应用程序上下文。因此,只要您的测试共享相同的配置(无论如何发现),加载上下文的潜在耗时过程只会发生一次。

使用测试配置的 `main` 方法

通常,由 @SpringBootTest 发现的测试配置将是您的主 @SpringBootApplication。在大多数结构良好的应用程序中,此配置类还将包含用于启动应用程序的 main 方法。

例如,以下是典型 Spring Boot 应用程序的常见代码模式

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);
	}

}

在上面的示例中,main 方法除了委托给 SpringApplication.run 之外什么也不做。但是,可以拥有更复杂的 main 方法,该方法在调用 SpringApplication.run 之前应用自定义项。

例如,以下是一个更改横幅模式并设置其他配置文件的应用程序

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);
	}

}

由于 main 方法中的自定义项可能会影响生成的 ApplicationContext,因此您可能还想使用 main 方法来创建测试中使用的 ApplicationContext。默认情况下,@SpringBootTest 不会调用您的 main 方法,而是直接使用该类本身来创建 ApplicationContext

如果要更改此行为,可以将 @SpringBootTestuseMainMethod 属性更改为 UseMainMethod.ALWAYSUseMainMethod.WHEN_AVAILABLE。当设置为 ALWAYS 时,如果找不到 main 方法,则测试将失败。当设置为 WHEN_AVAILABLE 时,如果 main 方法可用,则将使用它,否则将使用标准加载机制。

例如,以下测试将调用 MyApplicationmain 方法来创建 ApplicationContext。如果 main 方法设置了其他配置文件,那么这些配置文件将在 ApplicationContext 启动时处于活动状态。

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() {
		// ...
	}

}

排除测试配置

如果您的应用程序使用组件扫描(例如,如果您使用 @SpringBootApplication@ComponentScan),您可能会发现您仅为特定测试创建的顶级配置类意外地被到处拾取。

正如我们之前所见@TestConfiguration 可以用在测试的内部类上,以自定义主配置。@TestConfiguration 也可以用在顶层类上。这样做表示该类不应该被扫描发现。然后,您可以像以下示例所示那样显式导入该类。

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() {
		// ...
	}

}
如果您直接使用 @ComponentScan(即,不是通过 @SpringBootApplication),则需要向其注册 TypeExcludeFilter。有关详细信息,请参阅Javadoc
导入的 @TestConfiguration 比内部类 @TestConfiguration 更早地被处理,并且导入的 @TestConfiguration 将在通过组件扫描找到的任何配置之前被处理。一般来说,这种排序上的差异没有明显的影响,但如果您依赖于 bean 覆盖,则需要注意这一点。

使用应用程序参数

如果您的应用程序期望参数,您可以使用 args 属性让 @SpringBootTest 注入它们。

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");
	}

}

使用模拟环境进行测试

默认情况下,@SpringBootTest 不会启动服务器,而是为测试 Web 端点设置一个模拟环境。

使用 Spring MVC,我们可以使用MockMvcWebTestClient 查询我们的 Web 端点,如以下示例所示

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 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 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,如以下示例所示

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");
	}

}

在模拟环境中进行测试通常比使用完整的 servlet 容器运行更快。但是,由于模拟发生在 Spring MVC 层,因此依赖于更低级别 servlet 容器行为的代码无法直接使用 MockMvc 进行测试。

例如,Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。这意味着,虽然您可以测试您的 MVC 层按预期抛出和处理异常,但您无法直接测试是否渲染了特定的自定义错误页面。如果您需要测试这些更低级别的关注点,您可以启动一个完全运行的服务器,如下一节所述。

使用运行的服务器进行测试

如果您需要启动一个完整的运行服务器,我们建议您使用随机端口。如果您使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT),每次测试运行时都会随机选择一个可用端口。

@LocalServerPort 注解可用于注入实际使用的端口到您的测试中。为了方便起见,需要对已启动服务器进行 REST 调用测试,还可以@Autowire一个WebTestClient,它会将相对链接解析到正在运行的服务器,并提供一个专门的 API 用于验证响应,如下例所示

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");
	}

}
WebTestClient 也可以与模拟环境一起使用,通过在您的测试类上添加@AutoConfigureWebTestClient注解,无需启动服务器。

此设置需要类路径上的spring-webflux。如果您无法或不愿添加 webflux,Spring Boot 还提供了一个TestRestTemplate工具

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");
	}

}

自定义 WebTestClient

要自定义WebTestClient bean,请配置一个WebTestClientBuilderCustomizer bean。任何此类 bean 都会使用用于创建WebTestClientWebTestClient.Builder进行调用。

使用 JMX

由于测试上下文框架会缓存上下文,因此默认情况下会禁用 JMX,以防止相同组件在同一域上注册。如果此类测试需要访问MBeanServer,请考虑将其标记为脏。

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");
		// ...
	}

}

使用观察

如果您在切片测试上添加@AutoConfigureObservability注解,它会自动配置一个ObservationRegistry

使用指标

无论您的类路径如何,除了内存支持之外,使用@SpringBootTest时不会自动配置度量注册表。

如果您需要将指标导出到不同的后端作为集成测试的一部分,请在测试中添加@AutoConfigureObservability注解。

如果您在切片测试上添加@AutoConfigureObservability注解,它会自动配置一个内存中的MeterRegistry。切片测试中的数据导出不支持@AutoConfigureObservability注解。

使用跟踪

无论您的类路径如何,使用@SpringBootTest时不会自动配置报告数据的跟踪组件。

如果您需要这些组件作为集成测试的一部分,请在测试中添加@AutoConfigureObservability注解。

如果您创建了自己的报告组件(例如自定义的 SpanExporterSpanHandler),并且您不希望它们在测试中处于活动状态,您可以使用 @ConditionalOnEnabledTracing 注解来禁用它们。

如果您使用 @AutoConfigureObservability 注解 切片测试,它会自动配置一个无操作的 Tracer。使用 @AutoConfigureObservability 注解不支持切片测试中的数据导出。

模拟和间谍 Bean

在运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,您可能有一个对某些远程服务的 facade,该服务在开发期间不可用。当您想要模拟在真实环境中难以触发的故障时,模拟也很有用。

Spring Boot 包含一个 @MockBean 注解,可用于为 ApplicationContext 中的 Bean 定义 Mockito 模拟。您可以使用该注解添加新 Bean 或替换单个现有 Bean 定义。该注解可以直接用于测试类、测试中的字段或 @Configuration 类和字段。当用于字段时,创建的模拟实例也会被注入。模拟 Bean 在每个测试方法之后都会自动重置。

如果您的测试使用 Spring Boot 的某个测试注解(例如 @SpringBootTest),则此功能会自动启用。要将此功能与其他安排一起使用,必须显式添加侦听器,如以下示例所示

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 方法来创建和配置模拟。

此外,您可以使用 @SpyBean 将任何现有 Bean 包装在 Mockito spy 中。有关完整详细信息,请参阅 Javadoc

虽然 Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但使用 @MockBean@SpyBean 会影响缓存键,这很可能会增加上下文的数量。
如果您使用 @SpyBean 来监视具有 @Cacheable 方法的 Bean,这些方法按名称引用参数,则您的应用程序必须使用 -parameters 编译。这确保了参数名称在 Bean 被监视后可用于缓存基础设施。
当您使用@SpyBean来监控由 Spring 代理的 Bean 时,您可能需要在某些情况下移除 Spring 的代理,例如在使用givenwhen设置期望时。使用AopTestUtils.getTargetObject(yourProxiedSpy)来做到这一点。

自动配置测试

Spring Boot 的自动配置系统非常适合应用程序,但有时对于测试来说可能有点过分。通常情况下,只加载测试应用程序“切片”所需的部分配置会有所帮助。例如,您可能希望测试 Spring MVC 控制器是否正确映射 URL,并且您不希望在这些测试中涉及数据库调用,或者您可能希望测试 JPA 实体,并且您对这些测试运行时的 Web 层不感兴趣。

spring-boot-test-autoconfigure 模块包含许多注释,可用于自动配置此类“切片”。它们的工作方式都类似,提供一个@…​Test注释来加载ApplicationContext,以及一个或多个@AutoConfigure…​注释,可用于自定义自动配置设置。

每个切片将组件扫描限制在适当的组件,并加载一组非常有限的自动配置类。如果您需要排除其中一个,大多数@…​Test注释都提供了一个excludeAutoConfiguration属性。或者,您可以使用@ImportAutoConfiguration#exclude
不支持在一个测试中使用多个@…​Test注释来包含多个“切片”。如果您需要多个“切片”,请选择一个@…​Test注释,并手动包含其他“切片”的@AutoConfigure…​注释。
也可以将@AutoConfigure…​注释与标准@SpringBootTest注释一起使用。如果您对“切片”应用程序不感兴趣,但希望使用一些自动配置的测试 Bean,则可以使用此组合。

自动配置 JSON 测试

为了测试对象 JSON 序列化和反序列化是否按预期工作,您可以使用@JsonTest注释。@JsonTest自动配置可用的支持 JSON 映射器,它可以是以下库之一

  • Jackson ObjectMapper、任何@JsonComponent Bean 和任何 Jackson Module

  • Gson

  • Jsonb

@JsonTest启用的自动配置列表可以在附录中找到

如果您需要配置自动配置的元素,可以使用@AutoConfigureJsonTesters注释。

Spring Boot 包含基于 AssertJ 的帮助程序,它们与 JSONAssert 和 JsonPath 库一起使用,以检查 JSON 是否按预期出现。JacksonTesterGsonTesterJsonbTesterBasicJsonTester类可分别用于 Jackson、Gson、Jsonb 和字符串。在使用@JsonTest时,测试类上的任何帮助程序字段都可以@Autowired。以下示例显示了 Jackson 的测试类

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");
	}

}
JSON 辅助类也可以直接在标准单元测试中使用。为此,如果您不使用 @JsonTest,请在您的 @Before 方法中调用辅助类的 initFields 方法。

如果您使用 Spring Boot 的基于 AssertJ 的辅助程序来断言给定 JSON 路径上的数字值,您可能无法使用 isEqualTo,具体取决于类型。相反,您可以使用 AssertJ 的 satisfies 来断言该值是否与给定条件匹配。例如,以下示例断言实际数字是一个浮点值,接近 0.15,偏移量为 0.01

	@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)));
	}

自动配置的 Spring MVC 测试

要测试 Spring MVC 控制器是否按预期工作,请使用 @WebMvcTest 注解。@WebMvcTest 自动配置 Spring MVC 基础设施,并将扫描的 bean 限制为 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterFilterHandlerInterceptorWebMvcConfigurerWebMvcRegistrationsHandlerMethodArgumentResolver。当使用 @WebMvcTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。

@WebMvcTest 启用的自动配置设置列表可以在 附录中找到
如果您需要注册额外的组件,例如 Jackson Module,您可以通过在测试中使用 @Import 来导入额外的配置类。

通常,@WebMvcTest 仅限于单个控制器,并与 @MockBean 结合使用,为所需的协作者提供模拟实现。

@WebMvcTest 还自动配置 MockMvc。Mock MVC 提供了一种强大的方法,可以快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。

您也可以在非 @WebMvcTest(例如 @SpringBootTest)中使用 @AutoConfigureMockMvc 注解来自动配置 MockMvc。以下示例使用 MockMvc
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.features.testing.springbootapplications.springmvctests.UserVehicleController;
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"));
	}

}
如果您需要配置自动配置的元素(例如,当应应用 servlet 过滤器时),您可以在 @AutoConfigureMockMvc 注解中使用属性。

如果您使用 HtmlUnit 和 Selenium,自动配置还提供 HtmlUnit WebClient bean 和/或 Selenium WebDriver bean。以下示例使用 HtmlUnit

import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.features.testing.springbootapplications.springmvctests.UserVehicleController;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {

	@Autowired
	private WebClient webClient;

	@MockBean
	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");
	}

}
默认情况下,Spring Boot 将 WebDriver bean 放置在特殊的“范围”中,以确保驱动程序在每次测试后退出,并且会注入一个新实例。如果您不希望这种行为,可以在您的 WebDriver @Bean 定义中添加 @Scope("singleton")
Spring Boot 创建的 webDriver 范围将替换任何具有相同名称的用户定义范围。如果您定义了自己的 webDriver 范围,您可能会发现当您使用 @WebMvcTest 时它将停止工作。

如果您的类路径上有 Spring Security,@WebMvcTest 也会扫描 WebSecurityConfigurer bean。对于此类测试,您可以使用 Spring Security 的测试支持,而不是完全禁用安全性。有关如何使用 Spring Security 的 MockMvc 支持的更多详细信息,请参见本使用 Spring Security 进行测试 如何操作部分。

有时编写 Spring MVC 测试还不够;Spring Boot 可以帮助您运行 带有实际服务器的端到端完整测试

自动配置的 Spring WebFlux 测试

要测试 Spring WebFlux 控制器是否按预期工作,您可以使用 @WebFluxTest 注解。@WebFluxTest 会自动配置 Spring WebFlux 基础设施,并将扫描的 bean 限制为 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterWebFilterWebFluxConfigurer。使用 @WebFluxTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。

可以在 附录中 找到由 @WebFluxTest 启用的自动配置列表。
如果您需要注册额外的组件,例如 Jackson Module,则可以在测试中使用 @Import 导入其他配置类。

通常,@WebFluxTest 仅限于单个控制器,并与 @MockBean 注解结合使用,为所需的协作者提供模拟实现。

@WebFluxTest 还会自动配置 WebTestClient,它提供了一种强大的方法,可以快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。

您也可以在非 @WebFluxTest(例如 @SpringBootTest)中使用 @AutoConfigureWebTestClient 注解自动配置 WebTestClient。以下示例显示了一个使用 @WebFluxTestWebTestClient 的类。
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.features.testing.springbootapplications.springwebfluxtests.UserVehicleController;
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() {
		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");
	}

}
此设置仅受 WebFlux 应用程序支持,因为目前在模拟 Web 应用程序中使用 WebTestClient 仅适用于 WebFlux。
@WebFluxTest 无法检测到通过功能性 Web 框架注册的路由。要测试上下文中的 RouterFunction bean,请考虑使用 @Import 自己导入 RouterFunction,或使用 @SpringBootTest
@WebFluxTest 无法检测到注册为 SecurityWebFilterChain 类型 @Bean 的自定义安全配置。要将其包含在测试中,您需要使用 @Import@SpringBootTest 导入注册 bean 的配置。
有时编写 Spring WebFlux 测试还不够;Spring Boot 可以帮助您运行 带有实际服务器的完整端到端测试

自动配置的 Spring GraphQL 测试

Spring GraphQL 提供了一个专门的测试支持模块;您需要将其添加到您的项目中

Maven
<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>
Gradle
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 控制器@GraphQlTest 自动配置 Spring GraphQL 基础设施,无需任何传输或服务器参与。这将扫描的 bean 限制为 @ControllerRuntimeWiringConfigurerJsonComponentConverterGenericConverterDataFetcherExceptionResolverInstrumentationGraphQlSourceBuilderCustomizer。使用 @GraphQlTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 包含 @ConfigurationProperties bean。

可以在 附录中找到@GraphQlTest 启用的自动配置列表。

通常,@GraphQlTest 仅限于一组控制器,并与 @MockBean 注解结合使用以提供所需协作者的模拟实现。

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!");
	}

}

@SpringBootTest 测试是完整的集成测试,涉及整个应用程序。当使用随机端口或定义的端口时,会配置一个实时服务器,并且会自动贡献一个 HttpGraphQlTester bean,以便您可以使用它来测试您的服务器。当配置 MOCK 环境时,您还可以通过使用 @AutoConfigureHttpGraphQlTester 注解您的测试类来请求 HttpGraphQlTester bean

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!");
	}

}

自动配置的 Data Cassandra 测试

您可以使用 @DataCassandraTest 测试 Cassandra 应用程序。默认情况下,它会配置一个 CassandraTemplate,扫描 @Table 类,并配置 Spring Data Cassandra 存储库。使用 @DataCassandraTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 包含 @ConfigurationProperties bean。(有关使用 Cassandra 与 Spring Boot 的更多信息,请参阅 "Cassandra"。)

@DataCassandraTest 启用的自动配置设置列表可以在附录中找到

以下示例展示了在 Spring Boot 中使用 Cassandra 测试的典型设置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;

@DataCassandraTest
class MyDataCassandraTests {

	@Autowired
	private SomeRepository repository;

}

自动配置的 Data Couchbase 测试

您可以使用 @DataCouchbaseTest 来测试 Couchbase 应用程序。默认情况下,它会配置一个 CouchbaseTemplateReactiveCouchbaseTemplate,扫描 @Document 类,并配置 Spring Data Couchbase 存储库。当使用 @DataCouchbaseTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Couchbase 与 Spring Boot 的更多信息,请参阅本章前面部分的“Couchbase”。)

@DataCouchbaseTest 启用的自动配置设置列表可以在附录中找到

以下示例展示了在 Spring Boot 中使用 Couchbase 测试的典型设置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;

@DataCouchbaseTest
class MyDataCouchbaseTests {

	@Autowired
	private SomeRepository repository;

	// ...

}

自动配置的 Data Elasticsearch 测试

您可以使用 @DataElasticsearchTest 来测试 Elasticsearch 应用程序。默认情况下,它会配置一个 ElasticsearchRestTemplate,扫描 @Document 类,并配置 Spring Data Elasticsearch 存储库。当使用 @DataElasticsearchTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Elasticsearch 与 Spring Boot 的更多信息,请参阅本章前面部分的“Elasticsearch”。)

@DataElasticsearchTest 启用的自动配置设置列表可以在附录中找到

以下示例展示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;

@DataElasticsearchTest
class MyDataElasticsearchTests {

	@Autowired
	private SomeRepository repository;

	// ...

}

自动配置的 Data JPA 测试

您可以使用 @DataJpaTest 注解来测试 JPA 应用程序。默认情况下,它会扫描 @Entity 类并配置 Spring Data JPA 存储库。如果类路径上存在嵌入式数据库,它也会配置一个。默认情况下,通过将 spring.jpa.show-sql 属性设置为 true 来记录 SQL 查询。可以使用注解的 showSql 属性禁用此功能。

当使用 @DataJpaTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。

可以在 附录 中找到 @DataJpaTest 启用的自动配置设置列表。

默认情况下,数据 JPA 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参阅 Spring 框架参考文档中的 相关部分。如果您不希望这样,可以禁用测试或整个类的交易管理,如下所示

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 {

	// ...

}

数据 JPA 测试还可以注入 TestEntityManager bean,它提供了一种替代标准 JPA EntityManager 的方法,专门为测试而设计。

通过添加 @AutoConfigureTestEntityManager,也可以将 TestEntityManager 自动配置到任何基于 Spring 的测试类中。这样做时,请确保您的测试在事务中运行,例如通过在您的测试类或方法上添加 @Transactional

如果您需要,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() {
		this.entityManager.persist(new User("sboot", "1234"));
		User user = this.repository.findByUsername("sboot");
		assertThat(user.getUsername()).isEqualTo("sboot");
		assertThat(user.getEmployeeNumber()).isEqualTo("1234");
	}

}

内存中的嵌入式数据库通常适用于测试,因为它们速度快且不需要任何安装。但是,如果您希望针对真实数据库运行测试,可以使用 @AutoConfigureTestDatabase 注解,如以下示例所示

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 {

	// ...

}

自动配置的 JDBC 测试

@JdbcTest@DataJpaTest 类似,但适用于仅需要 DataSource 且不使用 Spring Data JDBC 的测试。默认情况下,它配置了一个内存中的嵌入式数据库和一个 JdbcTemplate。当使用 @JdbcTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。

可以在 附录 中找到 @JdbcTest 启用的自动配置列表。

默认情况下,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 注解。(请参阅“自动配置的数据 JPA 测试”。)

自动配置的数据 JDBC 测试

@DataJdbcTest 类似于 @JdbcTest,但用于使用 Spring Data JDBC 存储库的测试。默认情况下,它配置了一个内存中的嵌入式数据库、一个 JdbcTemplate 和 Spring Data JDBC 存储库。当使用 @DataJdbcTest 注解时,只扫描 AbstractJdbcConfiguration 子类,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。

可以在附录中找到@DataJdbcTest 启用的自动配置列表。

默认情况下,数据 JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参阅 Spring 框架参考文档中的相关部分。如果您不希望这样,可以像JDBC 示例中所示那样为测试或整个测试类禁用事务管理。

如果您希望您的测试针对真实数据库运行,您可以像使用 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(请参阅“自动配置的数据 JPA 测试”。)

自动配置的数据 R2DBC 测试

@DataR2dbcTest 类似于 @DataJdbcTest,但用于使用 Spring Data R2DBC 存储库的测试。默认情况下,它配置了一个内存中的嵌入式数据库、一个 R2dbcEntityTemplate 和 Spring Data R2DBC 存储库。当使用 @DataR2dbcTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。

@DataR2dbcTest 启用的自动配置列表可以在 附录 中找到。

默认情况下,Data R2DBC 测试是非事务性的。

如果您希望您的测试针对真实数据库运行,您可以像使用 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(请参阅“自动配置的数据 JPA 测试”。)

自动配置的 jOOQ 测试

您可以像使用 @JdbcTest 一样使用 @JooqTest,但用于与 jOOQ 相关的测试。由于 jOOQ 严重依赖于与数据库模式相对应的基于 Java 的模式,因此使用现有的 DataSource。如果您想用内存数据库替换它,可以使用 @AutoConfigureTestDatabase 覆盖这些设置。(有关使用 jOOQ 与 Spring Boot 的更多信息,请参阅 "使用 jOOQ"。)当使用 @JooqTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 包含 @ConfigurationProperties bean。

@JooqTest 启用的自动配置列表可以在 附录 中找到。

@JooqTest 配置了一个 DSLContext。以下示例显示了 @JooqTest 注解的使用情况

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;

	// ...

}

默认情况下,jOOQ 测试是事务性的,并在每个测试结束时回滚。如果您不希望这样做,可以为测试或整个测试类禁用事务管理,如 JDBC 示例中所示

自动配置的 Data MongoDB 测试

您可以使用 @DataMongoTest 测试 MongoDB 应用程序。默认情况下,它配置一个 MongoTemplate,扫描 @Document 类,并配置 Spring Data MongoDB 存储库。当使用 @DataMongoTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 包含 @ConfigurationProperties bean。(有关使用 MongoDB 与 Spring Boot 的更多信息,请参阅 "MongoDB"。)

@DataMongoTest 启用的自动配置设置列表可以在 附录 中找到。

以下类显示了 @DataMongoTest 注解的使用情况

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;

	// ...

}

自动配置的 Data Neo4j 测试

您可以使用 @DataNeo4jTest 来测试 Neo4j 应用程序。默认情况下,它会扫描 @Node 类,并配置 Spring Data Neo4j 存储库。当使用 @DataNeo4jTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Spring Boot 与 Neo4J 的更多信息,请参见 "Neo4j"。)

@DataNeo4jTest 启用的自动配置设置列表可以在 附录中找到

以下示例展示了在 Spring Boot 中使用 Neo4J 测试的典型设置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;

@DataNeo4jTest
class MyDataNeo4jTests {

	@Autowired
	private SomeRepository repository;

	// ...

}

默认情况下,Data Neo4j 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring Framework 参考文档中的 相关部分。如果您不想要这种行为,可以禁用测试或整个类的交易管理,如下所示

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 注解的使用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;

@DataRedisTest
class MyDataRedisTests {

	@Autowired
	private SomeRepository repository;

	// ...

}

自动配置的 Data LDAP 测试

您可以使用 @DataLdapTest 来测试 LDAP 应用程序。默认情况下,它会配置一个内存中的嵌入式 LDAP(如果可用),配置一个 LdapTemplate,扫描 @Entry 类,并配置 Spring Data LDAP 存储库。当使用 @DataLdapTest 注解时,不会扫描常规的 @Component@ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Spring Boot 与 LDAP 的更多信息,请参见 "LDAP"。)

@DataLdapTest 启用的自动配置设置列表可以在 附录中找到

以下示例展示了 @DataLdapTest 注解的使用

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;

	// ...

}

内存中的嵌入式 LDAP 通常适用于测试,因为它速度快且不需要任何开发人员安装。但是,如果您希望针对真实的 LDAP 服务器运行测试,则应排除嵌入式 LDAP 自动配置,如以下示例所示

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 启用的自动配置设置列表。

要测试的特定 bean 应使用 @RestClientTestvaluecomponents 属性指定。

当在测试的 bean 中使用 RestTemplateBuilder 并且在构建 RestTemplate 时调用了 RestTemplateBuilder.rootUri(String rootUri) 时,应从 MockRestServiceServer 预期中省略根 URI,如以下示例所示

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");
	}

}

当在测试的 bean 中使用 RestClient.Builder,或当使用 RestTemplateBuilder 而不调用 rootUri(String rootURI) 时,必须在 MockRestServiceServer 预期中使用完整 URI,如以下示例所示

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");
	}

}

自动配置的 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 中的主机、方案和端口。

使用 Mock MVC 的自动配置的 Spring REST Docs 测试

@AutoConfigureRestDocs 自定义 MockMvc bean 以在测试基于 servlet 的 Web 应用程序时使用 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.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。自动配置使用此结果处理程序调用 alwaysDo,从而导致每个 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}");
	}

}

使用 WebTestClient 自动配置 Spring REST Docs 测试

@AutoConfigureRestDocs 也可以与 WebTestClient 一起使用,用于测试响应式 Web 应用程序。您可以使用 @Autowired 注入它,并在测试中像使用 @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"));
	}

}

如果您需要比 @AutoConfigureRestDocs 属性提供的更多对 Spring REST Docs 配置的控制,可以使用 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}"));
	}

}

使用 REST Assured 自动配置 Spring REST Docs 测试

@AutoConfigureRestDocs 为您的测试提供了一个 RequestSpecification bean,该 bean 预先配置为使用 Spring REST Docs。您可以使用 @Autowired 注入它,并在测试中像使用 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.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));
	}

}

如果您需要比 @AutoConfigureRestDocs 属性提供的更多对 Spring REST Docs 配置的控制,可以使用 RestDocsRestAssuredConfigurationCustomizer bean,如下例所示

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());
	}

}

自动配置的 Spring Web Services 测试

自动配置的 Spring Web Services 客户端测试

您可以使用 @WebServiceClientTest 测试使用 Spring Web Services 项目调用 Web 服务的应用程序。默认情况下,它会配置一个模拟 WebServiceServer bean 并自动自定义您的 WebServiceTemplateBuilder。(有关使用 Spring Boot 的 Web 服务的更多信息,请参见 "Web Services"。)

@WebServiceClientTest 启用的自动配置设置列表可以在 附录中找到

以下示例展示了 @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("<request/>")))
			.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
		assertThat(this.someWebService.test())
			.extracting(Response::getStatus)
			.isEqualTo(200);
	}

}

自动配置的 Spring Web Services 服务器测试

您可以使用 @WebServiceServerTest 测试使用 Spring Web Services 项目实现 Web 服务的应用程序。默认情况下,它会配置一个 MockWebServiceClient bean,可用于调用您的 Web 服务端点。(有关使用 Spring Boot 的 Web 服务的更多信息,请参见 "Web Services"。)

@WebServiceServerTest 启用的自动配置设置列表可以在附录中找到

以下示例展示了 @WebServiceServerTest 注解的使用。

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.features.testing.springbootapplications.autoconfiguredwebservices.server.ExampleEndpoint;
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>")));
	}

}

附加自动配置和切片

每个切片提供一个或多个 @AutoConfigure…​ 注解,这些注解定义了应包含在切片中的自动配置。可以通过创建自定义 @AutoConfigure…​ 注解或在测试中添加 @ImportAutoConfiguration 来在逐个测试的基础上添加额外的自动配置,如下例所示。

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 中存储的文件中注册额外的自动配置,为任何切片注解的使用添加额外的自动配置,如下例所示。

META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports
com.example.IntegrationAutoConfiguration

在本例中,com.example.IntegrationAutoConfiguration 在每个使用 @JdbcTest 注解的测试中启用。

您可以在此文件中使用 # 注释。
只要切片或 @AutoConfigure…​ 注解使用 @ImportAutoConfiguration 进行元注解,就可以以这种方式对其进行自定义。

用户配置和切片

如果您以合理的方式构建代码,您的 @SpringBootApplication 类将默认用作测试的配置。

因此,不要在应用程序的主类中添加特定于其功能特定区域的配置设置非常重要。

假设您正在使用 Spring Data MongoDB,您依赖于它的自动配置,并且您已启用审计。您可以按如下方式定义您的 @SpringBootApplication

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {

	// ...

}

由于此类是测试的源配置,因此任何切片测试实际上都尝试启用 Mongo 审计,这绝对不是您想要做的。建议的方法是将该区域特定的配置移动到与您的应用程序位于同一级别的单独 @Configuration 类中,如下例所示。

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;

@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {

	// ...

}
根据应用程序的复杂性,您可能只有一个用于自定义的 @Configuration 类,或者每个领域有一个类。后一种方法允许您在必要时使用 @Import 注解在一个测试中启用它。有关何时可能需要为切片测试启用特定 @Configuration 类的更多详细信息,请参阅此操作指南部分

测试切片会排除@Configuration类,使其不参与扫描。例如,对于@WebMvcTest,以下配置将不会将给定的WebMvcConfigurer bean 包含在测试切片加载的应用程序上下文中。

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() {
			// ...
		};
	}

}

但是,以下配置将导致自定义WebMvcConfigurer 被测试切片加载。

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

	// ...

}

另一个容易混淆的地方是类路径扫描。假设,虽然您以合理的方式构建了代码,但您需要扫描额外的包。您的应用程序可能类似于以下代码。

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

	// ...

}

这样做实际上会覆盖默认的组件扫描指令,并产生副作用,即无论您选择哪个切片,都会扫描这两个包。例如,@DataJpaTest 似乎突然开始扫描应用程序的组件和用户配置。同样,将自定义指令移到单独的类中是解决此问题的有效方法。

如果这对您来说不可行,您可以在测试层次结构中的某个位置创建一个@SpringBootConfiguration,以便使用它。或者,您可以为您的测试指定一个源,这将禁用查找默认源的行为。

使用 Spock 测试 Spring Boot 应用程序

Spock 2.2 或更高版本可用于测试 Spring Boot 应用程序。为此,请将 Spock 的 spock-spring 模块的 -groovy-4.0 版本的依赖项添加到应用程序的构建中。spock-spring 将 Spring 的测试框架集成到 Spock 中。有关更多详细信息,请参阅Spock Spring 模块的文档