测试 Spring Boot 应用

Spring Boot 应用是一个 Spring ApplicationContext,所以除了通常对普通 Spring 上下文所做的操作外,不需要做任何特殊的事情来测试它。

只有当你使用 SpringApplication 创建它时,Spring Boot 的外部属性、日志记录和其他功能才会默认安装在上下文中。

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客户端和服务器在单独的线程中运行,因此在单独的事务中运行。在这种情况下,在服务器上启动的任何事务都不会回滚。
如果你的应用程序使用不同的管理服务器端口,则带有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的基础组件扫描配置定义了排除过滤器,用于确保切片按预期工作。如果你在@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之外什么也不做。但是,可以拥有更复杂的main方法,在调用SpringApplication.run之前应用自定义设置。

例如,这是一个更改横幅模式并设置附加配置文件的应用程序

  • 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

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

例如,以下测试将调用MyApplicationmain方法以创建ApplicationContext。如果main方法设置了附加配置文件,则这些配置文件将在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覆盖,则需要注意这一点。

使用应用程序参数

如果你的应用程序需要参数,则可以使用args属性让@SpringBootTest注入它们。

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

}

使用模拟环境进行测试

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

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

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

}
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.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

	@Test
	fun testWithMockMvc(@Autowired mvc: MockMvc) {
		mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
			.andExpect(MockMvcResultMatchers.content().string("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),则每次运行测试时都会随机选择一个可用的端口。

@LocalServerPort注解可用于将实际使用的端口注入到你的测试中。为方便起见,需要向启动的服务器发出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")
	}

}
WebTestClient也可以与模拟环境一起使用,无需运行服务器,方法是用@AutoConfigureWebTestClient注释你的测试类。

此设置需要类路径上的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 都将使用用于创建WebTestClientWebTestClient.Builder进行调用。

使用JMX

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

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

}

使用观测

如果你用@AutoConfigureObservability注释切片测试,它会自动配置一个ObservationRegistry

使用指标

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

如果你需要在集成测试中将指标导出到不同的后端,请使用@AutoConfigureObservability对其进行注释。

如果你用@AutoConfigureObservability注释切片测试,它会自动配置一个内存中的MeterRegistry。使用@AutoConfigureObservability注释不支持切片测试中的数据导出。

使用跟踪

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

如果你需要这些组件作为集成测试的一部分,请使用@AutoConfigureObservability注释测试。

如果你创建了自己的报告组件(例如自定义SpanExporterSpanHandler),并且不想在测试中激活它们,可以使用@ConditionalOnEnabledTracing注释来禁用它们。

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

模拟和侦听Bean

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

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

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

  • Java

  • Kotlin

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 {

	// ...

}
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。

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

}
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.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean

@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {

	@Test
	fun exampleTest() {
		given(remoteService.value).willReturn("spring")
		val reverse = reverser.reverseValue // Calls injected RemoteService
		assertThat(reverse).isEqualTo("gnirps")
	}

}
@MockBean不能用于模拟在应用程序上下文刷新期间使用的bean的行为。在执行测试时,应用程序上下文刷新已完成,此时配置模拟行为为时已晚。在这种情况下,我们建议使用@Bean方法来创建和配置模拟对象。

此外,您可以使用@SpyBean将任何现有的bean包装到Mockito的spy中。有关完整详细信息,请参阅SpyBean API文档。

虽然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的测试类。

  • 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,请在您的@Before方法中调用辅助程序的initFields方法。

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

  • 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@JsonComponentConverterGenericConverterFilterHandlerInterceptorWebMvcConfigurerWebMvcRegistrationsHandlerMethodArgumentResolver。使用@WebMvcTest注解时,不会扫描常规的@Component@ConfigurationProperties bean。可以使用@EnableConfigurationProperties来包含@ConfigurationProperties bean。

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

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

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

您还可以通过使用@AutoConfigureMockMvc对其进行注释,在非@WebMvcTest(例如@SpringBootTest)中自动配置MockMvc。以下示例使用MockMvc
  • 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.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"));
	}

}
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.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvc) {

	@MockBean
	lateinit var userVehicleService: UserVehicleService

	@Test
	fun testExample() {
		given(userVehicleService.getVehicleDetails("sboot"))
			.willReturn(VehicleDetails("Honda", "Civic"))
		mvc.perform(MockMvcRequestBuilders.get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
			.andExpect(MockMvcResultMatchers.status().isOk)
			.andExpect(MockMvcResultMatchers.content().string("Honda Civic"))
	}

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

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

  • Java

  • Kotlin

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

}
import com.gargoylesoftware.htmlunit.WebClient
import com.gargoylesoftware.htmlunit.html.HtmlPage
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.boot.test.mock.mockito.MockBean

@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {

	@MockBean
	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 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服务器。

您还可以通过使用@AutoConfigureWebTestClient对其进行注释,在非@WebFluxTest(例如@SpringBootTest)中自动配置WebTestClient。以下示例显示一个同时使用@WebFluxTestWebTestClient的类。
  • 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.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");
	}

}
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.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
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) {

	@MockBean
	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应用程序支持,因为目前仅在WebFlux中,在模拟的Web应用程序中使用WebTestClient才有效。
@WebFluxTest 无法检测到通过函数式 Web 框架注册的路由。要在上下文中测试RouterFunction bean,请考虑使用@Import导入您的RouterFunction,或使用@SpringBootTest
@WebFluxTest 无法检测到注册为SecurityWebFilterChain 类型@Bean 的自定义安全配置。要在测试中包含它,您需要使用@Import导入注册该 bean 的配置,或使用@SpringBootTest
有时编写 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 注解结合使用,以提供所需协作者的模拟实现。

  • 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 应用程序。默认情况下,它配置CouchbaseTemplateReactiveCouchbaseTemplate,扫描@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 应用程序。默认情况下,它配置ElasticsearchRestTemplate,扫描@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 的方法,该方法专门为测试而设计。

也可以通过添加@AutoConfigureTestEntityManagerTestEntityManager自动配置到任何基于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。

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

默认情况下,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

如果您更喜欢针对真实数据库运行测试,则可以使用@AutoConfigureTestDatabase 注解,方法与@DataJpaTest 相同。(参见自动配置的 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 示例中所示那样禁用测试或整个测试类的交易管理。

如果您更喜欢针对真实数据库运行测试,则可以使用@AutoConfigureTestDatabase 注解,方法与@DataJpaTest 相同。(参见自动配置的 Data JPA 测试。)

自动配置的 Data R2DBC 测试

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

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

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

如果您更喜欢针对真实数据库运行测试,则可以使用@AutoConfigureTestDatabase 注解,方法与@DataJpaTest 相同。(参见自动配置的 Data JPA 测试。)

自动配置的 jOOQ 测试

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

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

@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。(有关使用MongoDB与Spring Boot的更多信息,请参阅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存储库。使用@DataNeo4jTest注解时,不会扫描常规的@Component@ConfigurationProperties bean。可以使用@EnableConfigurationProperties包含@ConfigurationProperties bean。(有关使用Neo4J与Spring Boot的更多信息,请参阅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。(有关使用Redis与Spring Boot的更多信息,请参阅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。(有关使用LDAP与Spring Boot的更多信息,请参阅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启用的自动配置设置列表可以在附录中找到

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

当在被测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注解在您的测试中使用Spring REST Docs,使用Mock MVC、REST Assured或WebTestClient。它无需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时一样在测试中使用它,如下例所示

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

}
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.restdocs.mockmvc.MockMvcRestDocumentation
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

@WebMvcTest(UserController::class)
@AutoConfigureRestDocs
class MyUserDocumentationTests(@Autowired val mvc: MockMvc) {

	@Test
	fun listUsers() {
		mvc.perform(MockMvcRequestBuilders.get("/users").accept(MediaType.TEXT_PLAIN))
			.andExpect(MockMvcResultMatchers.status().isOk)
			.andDo(MockMvcRestDocumentation.document("list-users"))
	}

}

如果您需要比@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调用自动生成默认代码片段。以下示例显示了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测试

在测试反应式Web应用程序时,@AutoConfigureRestDocs也可以与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注入它,并像通常在使用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服务的应用程序。默认情况下,它配置一个模拟WebServiceServer bean并自动自定义您的WebServiceTemplateBuilder。(有关使用Spring Boot的Web服务的更多信息,请参阅Web服务。)

@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 服务。)

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

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

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

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

用户配置和切片

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

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

假设您正在使用 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注解在一个测试中启用它。有关何时可能需要为切片测试启用特定@Configuration类的更多详细信息,请参见此操作方法部分

测试切片会排除@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 {

	// ...

}

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

  • 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 {

	// ...

}

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

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

使用 Spock 测试 Spring Boot 应用程序

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