此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.4spring-doc.cn

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.4spring-doc.cn

Spring Boot 应用程序是 Spring ,因此除了通常使用普通 Spring 上下文所做的操作之外,无需执行任何特别操作来测试它。ApplicationContextspring-doc.cn

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

Spring Boot 提供了一个 Comments,当您需要 Spring Boot 功能时,可以将其用作标准 Comments 的替代方法。 Comments 的工作原理是通过 SpringApplication 创建测试中使用的ApplicationContext。 除了许多其他注释之外,还提供了用于测试应用程序的更具体切片的注释@SpringBootTestspring-test@ContextConfiguration@SpringBootTestspring-doc.cn

如果您使用的是 JUnit 4,请不要忘记也添加到您的测试中,否则注释将被忽略。 如果您使用的是 JUnit 5,则无需添加等效的 as,并且其他注释已经使用它进行了注释。@RunWith(SpringRunner.class)@ExtendWith(SpringExtension.class)@SpringBootTest@…​Test

默认情况下,不会启动服务器。 您可以使用 的 属性 of 进一步优化测试的运行方式:@SpringBootTestwebEnvironment@SpringBootTestspring-doc.cn

  • MOCK(默认) :加载 Web 并提供模拟 Web 环境。 使用此注释时,嵌入式服务器不会启动。 如果 Web 环境在 Classpath 上不可用,则此模式透明地回退到创建常规的 non-web 。 它可以与 @AutoConfigureMockMvc@AutoConfigureWebTestClient 结合使用,以便对 Web 应用程序进行基于模拟的测试。ApplicationContextApplicationContextspring-doc.cn

  • RANDOM_PORT:加载 并提供真实的 Web 环境。 嵌入式服务器启动并侦听随机端口。WebServerApplicationContextspring-doc.cn

  • DEFINED_PORT:加载 并提供真实的 Web 环境。 嵌入式服务器启动并侦听定义的端口(来自 )或默认端口 。WebServerApplicationContextapplication.properties8080spring-doc.cn

  • NONE:使用 但不提供任何 Web 环境(模拟或其他)加载。ApplicationContextSpringApplicationspring-doc.cn

如果您的测试是 ,则默认情况下,它会在每个测试方法结束时回滚事务。 但是,由于将这种安排与 OR 一起使用会隐式提供真实的 servlet 环境,因此 HTTP 客户端和服务器在单独的线程中运行,因此在单独的事务中运行。 在这种情况下,在服务器上启动的任何事务都不会回滚。@TransactionalRANDOM_PORTDEFINED_PORT
@SpringBootTest如果您的应用程序对 Management Server 使用不同的端口,则 with 还将在单独的随机端口上启动 Management Server。webEnvironment = WebEnvironment.RANDOM_PORT
默认情况下,Spring Boot 的外部属性、日志记录和其他功能仅在用于创建它时才会安装在上下文中。SpringApplication
如果您使用的是 JUnit 4,请不要忘记也添加到您的测试中,否则注释将被忽略。 如果您使用的是 JUnit 5,则无需添加等效的 as,并且其他注释已经使用它进行了注释。@RunWith(SpringRunner.class)@ExtendWith(SpringExtension.class)@SpringBootTest@…​Test
如果您的测试是 ,则默认情况下,它会在每个测试方法结束时回滚事务。 但是,由于将这种安排与 OR 一起使用会隐式提供真实的 servlet 环境,因此 HTTP 客户端和服务器在单独的线程中运行,因此在单独的事务中运行。 在这种情况下,在服务器上启动的任何事务都不会回滚。@TransactionalRANDOM_PORTDEFINED_PORT
@SpringBootTest如果您的应用程序对 Management Server 使用不同的端口,则 with 还将在单独的随机端口上启动 Management Server。webEnvironment = WebEnvironment.RANDOM_PORT

检测 Web 应用程序类型

如果 Spring MVC 可用,则配置基于常规 MVC 的应用程序上下文。 如果您只有 Spring WebFlux,我们将检测到该情况并配置基于 WebFlux 的应用程序上下文。spring-doc.cn

如果两者都存在,则 Spring MVC 优先。 如果要在此场景中测试反应式 Web 应用程序,则必须设置以下属性:spring.main.web-application-typespring-doc.cn

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,则可能习惯于使用来指定要加载的 Spring。 或者,您可能经常在测试中使用嵌套类。@ContextConfiguration(classes=…​)@Configuration@Configurationspring-doc.cn

在测试 Spring Boot 应用程序时,这通常不是必需的。 Spring Boot 的 Comments 会在您未明确定义主配置时自动搜索主配置。@*Testspring-doc.cn

搜索算法从包含测试的包开始工作,直到找到带有 或 注释的类。 只要你以合理的方式构建你的代码,你的主要配置通常会被找到。@SpringBootApplication@SpringBootConfigurationspring-doc.cn

如果使用 test annotation 来测试应用程序的更具体部分,则应避免在 main 方法的 application 类上添加特定于特定区域的配置设置。spring-doc.cn

的底层组件扫描配置定义了用于确保切片按预期工作的排除过滤器。 如果您在 -annotated 类上使用 explicit 指令,请注意这些过滤器将被禁用。 如果您正在使用切片,则应再次定义它们。@SpringBootApplication@ComponentScan@SpringBootApplicationspring-doc.cn

如果要自定义主配置,可以使用嵌套类。 与嵌套类不同,嵌套类将用于代替应用程序的主配置,而嵌套类则除了应用程序的主配置外,还使用嵌套类。@TestConfiguration@Configuration@TestConfigurationspring-doc.cn

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

如果使用 test annotation 来测试应用程序的更具体部分,则应避免在 main 方法的 application 类上添加特定于特定区域的配置设置。spring-doc.cn

的底层组件扫描配置定义了用于确保切片按预期工作的排除过滤器。 如果您在 -annotated 类上使用 explicit 指令,请注意这些过滤器将被禁用。 如果您正在使用切片,则应再次定义它们。@SpringBootApplication@ComponentScan@SpringBootApplicationspring-doc.cn

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

使用 Test Configuration Main 方法

通常,发现的测试配置将是您的主要 。 在大多数结构良好的应用程序中,此 configuration 类还将包括用于启动应用程序的方法。@SpringBootTest@SpringBootApplicationmainspring-doc.cn

例如,以下是典型 Spring Boot 应用程序的非常常见的 Code Pattern:spring-doc.cn

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

在上面的示例中,该方法除了委托 . 但是,可以使用更复杂的方法在调用 .mainSpringApplication.runmainSpringApplication.runspring-doc.cn

例如,下面是一个更改横幅模式并设置其他配置文件的应用程序:spring-doc.cn

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

由于方法中的自定义可能会影响结果,因此您可能还希望使用该方法在测试中创建 used。 默认情况下,不会调用您的方法,而是直接使用类本身来创建mainApplicationContextmainApplicationContext@SpringBootTestmainApplicationContextspring-doc.cn

如果要更改此行为,可以将 的属性更改为 或 。 设置为 时,如果找不到方法,则测试将失败。 设置为 时,如果可用,则使用方法,否则将使用标准加载机制。useMainMethod@SpringBootTestUseMainMethod.ALWAYSUseMainMethod.WHEN_AVAILABLEALWAYSmainWHEN_AVAILABLEmainspring-doc.cn

例如,以下测试将调用 of 的方法以创建 . 如果 main 方法设置了其他配置文件,则这些配置文件将在启动时处于活动状态。mainMyApplicationApplicationContextApplicationContextspring-doc.cn

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@ComponentScanspring-doc.cn

正如我们之前看到的,可以用于测试的内部类来自定义主要配置。 也可以在顶级类上使用。这样做表示不应通过扫描来选取该类。 然后,您可以在需要的地方显式导入该类,如以下示例所示:@TestConfiguration@TestConfigurationspring-doc.cn

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

}
如果直接使用 (即,不是通过 ),则需要向 它注册 。 有关详细信息,请参阅 API 文档。@ComponentScan@SpringBootApplicationTypeExcludeFilterTypeExcludeFilter
导入的 an 比 inner-class 更早处理,而 imported 将在通过组件扫描找到任何配置之前处理。 一般来说,这种 Sequences 的差异没有明显的影响,但是如果你依赖于 bean 覆盖,则需要注意这一点。@TestConfiguration@TestConfiguration@TestConfiguration
如果直接使用 (即,不是通过 ),则需要向 它注册 。 有关详细信息,请参阅 API 文档。@ComponentScan@SpringBootApplicationTypeExcludeFilterTypeExcludeFilter
导入的 an 比 inner-class 更早处理,而 imported 将在通过组件扫描找到任何配置之前处理。 一般来说,这种 Sequences 的差异没有明显的影响,但是如果你依赖于 bean 覆盖,则需要注意这一点。@TestConfiguration@TestConfiguration@TestConfiguration

使用应用程序参数

如果您的应用程序需要参数,则可以 使用 attribute 注入它们。@SpringBootTestargsspring-doc.cn

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {

	@Test
	void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
		assertThat(args.getOptionNames()).containsOnly("app.test");
		assertThat(args.getOptionValues("app.test")).containsOnly("one");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {

	@Test
	fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
		assertThat(args.optionNames).containsOnly("app.test")
		assertThat(args.getOptionValues("app.test")).containsOnly("one")
	}

}

使用 Mock 环境进行测试

默认情况下,不会启动服务器,而是设置用于测试 Web 端点的模拟环境。@SpringBootTestspring-doc.cn

借助 Spring MVC,我们可以使用 MockMvc 查询我们的 Web 端点。 有三种集成可用:spring-doc.cn

以下示例展示了可用的集成:spring-doc.cn

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

	@Test
	void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
		mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
	}

	// If AssertJ is on the classpath, you can use MockMvcTester
	@Test
	void testWithMockMvcTester(@Autowired MockMvcTester mvc) {
		assertThat(mvc.get().uri("/")).hasStatusOk().hasBodyTextEqualTo("Hello World");
	}

	// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
	@Test
	void testWithWebTestClient(@Autowired WebTestClient webClient) {
		webClient
				.get().uri("/")
				.exchange()
				.expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello World");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.assertj.MockMvcTester

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

	@Test
	fun testWithMockMvc(@Autowired mvc: MockMvcTester) {
		assertThat(mvc.get().uri("/")).hasStatusOk()
				.hasBodyTextEqualTo("Hello World")
	}

	// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient

	@Test
	fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
		webClient
				.get().uri("/")
				.exchange()
				.expectStatus().isOk
				.expectBody<String>().isEqualTo("Hello World")
	}

}
如果您只想关注 Web 图层,而不开始 完整 ,请考虑改用 @WebMvcTestApplicationContext

使用 Spring WebFlux 端点,你可以使用WebTestClient,如以下示例所示:spring-doc.cn

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 层,因此无法直接使用 MockMvc 测试依赖于较低级别 servlet 容器行为的代码。spring-doc.cn

例如, Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。 这意味着,虽然您可以测试 MVC 层是否按预期引发和处理异常,但不能直接测试是否呈现了特定的自定义错误页面。 如果需要测试这些较低级别的问题,可以按照下一节所述启动完全运行的服务器。spring-doc.cn

如果您只想关注 Web 图层,而不开始 完整 ,请考虑改用 @WebMvcTestApplicationContext

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

例如, Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。 这意味着,虽然您可以测试 MVC 层是否按预期引发和处理异常,但不能直接测试是否呈现了特定的自定义错误页面。 如果需要测试这些较低级别的问题,可以按照下一节所述启动完全运行的服务器。spring-doc.cn