|
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.4! |
|
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.4! |
Spring Boot 应用程序是 Spring ,因此除了通常使用普通 Spring 上下文所做的操作之外,无需执行任何特别操作来测试它。ApplicationContext
默认情况下,Spring Boot 的外部属性、日志记录和其他功能仅在用于创建它时才会安装在上下文中。SpringApplication |
Spring Boot 提供了一个 Comments,当您需要 Spring Boot 功能时,可以将其用作标准 Comments 的替代方法。
Comments 的工作原理是通过 SpringApplication 创建测试中使用的ApplicationContext。
除了许多其他注释之外,还提供了用于测试应用程序的更具体切片的注释。@SpringBootTestspring-test@ContextConfiguration@SpringBootTest
如果您使用的是 JUnit 4,请不要忘记也添加到您的测试中,否则注释将被忽略。
如果您使用的是 JUnit 5,则无需添加等效的 as,并且其他注释已经使用它进行了注释。@RunWith(SpringRunner.class)@ExtendWith(SpringExtension.class)@SpringBootTest@…Test |
默认情况下,不会启动服务器。
您可以使用 的 属性 of 进一步优化测试的运行方式:@SpringBootTestwebEnvironment@SpringBootTest
-
MOCK(默认) :加载 Web 并提供模拟 Web 环境。 使用此注释时,嵌入式服务器不会启动。 如果 Web 环境在 Classpath 上不可用,则此模式透明地回退到创建常规的 non-web 。 它可以与@AutoConfigureMockMvc或@AutoConfigureWebTestClient结合使用,以便对 Web 应用程序进行基于模拟的测试。ApplicationContextApplicationContext -
RANDOM_PORT:加载 并提供真实的 Web 环境。 嵌入式服务器启动并侦听随机端口。WebServerApplicationContext -
DEFINED_PORT:加载 并提供真实的 Web 环境。 嵌入式服务器启动并侦听定义的端口(来自 )或默认端口 。WebServerApplicationContextapplication.properties8080 -
NONE:使用 但不提供任何 Web 环境(模拟或其他)加载。ApplicationContextSpringApplication
如果您的测试是 ,则默认情况下,它会在每个测试方法结束时回滚事务。
但是,由于将这种安排与 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 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,则可能习惯于使用来指定要加载的 Spring。
或者,您可能经常在测试中使用嵌套类。@ContextConfiguration(classes=…)@Configuration@Configuration
在测试 Spring Boot 应用程序时,这通常不是必需的。
Spring Boot 的 Comments 会在您未明确定义主配置时自动搜索主配置。@*Test
搜索算法从包含测试的包开始工作,直到找到带有 或 注释的类。
只要你以合理的方式构建你的代码,你的主要配置通常会被找到。@SpringBootApplication@SpringBootConfiguration
|
如果使用 test annotation 来测试应用程序的更具体部分,则应避免在 main 方法的 application 类上添加特定于特定区域的配置设置。 的底层组件扫描配置定义了用于确保切片按预期工作的排除过滤器。
如果您在 -annotated 类上使用 explicit 指令,请注意这些过滤器将被禁用。
如果您正在使用切片,则应再次定义它们。 |
如果要自定义主配置,可以使用嵌套类。
与嵌套类不同,嵌套类将用于代替应用程序的主配置,而嵌套类则除了应用程序的主配置外,还使用嵌套类。@TestConfiguration@Configuration@TestConfiguration
| Spring 的测试框架在测试之间缓存应用程序上下文。 因此,只要您的测试共享相同的配置(无论它是如何发现的),加载上下文的潜在耗时过程只会发生一次。 |
|
如果使用 test annotation 来测试应用程序的更具体部分,则应避免在 main 方法的 application 类上添加特定于特定区域的配置设置。 的底层组件扫描配置定义了用于确保切片按预期工作的排除过滤器。
如果您在 -annotated 类上使用 explicit 指令,请注意这些过滤器将被禁用。
如果您正在使用切片,则应再次定义它们。 |
| Spring 的测试框架在测试之间缓存应用程序上下文。 因此,只要您的测试共享相同的配置(无论它是如何发现的),加载上下文的潜在耗时过程只会发生一次。 |
使用 Test Configuration Main 方法
通常,发现的测试配置将是您的主要 。
在大多数结构良好的应用程序中,此 configuration 类还将包括用于启动应用程序的方法。@SpringBootTest@SpringBootApplicationmain
例如,以下是典型 Spring Boot 应用程序的非常常见的 Code Pattern:
-
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)
}
在上面的示例中,该方法除了委托 .
但是,可以使用更复杂的方法在调用 .mainSpringApplication.runmainSpringApplication.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")
}
}
由于方法中的自定义可能会影响结果,因此您可能还希望使用该方法在测试中创建 used。
默认情况下,不会调用您的方法,而是直接使用类本身来创建mainApplicationContextmainApplicationContext@SpringBootTestmainApplicationContext
如果要更改此行为,可以将 的属性更改为 或 。
设置为 时,如果找不到方法,则测试将失败。
设置为 时,如果可用,则使用方法,否则将使用标准加载机制。useMainMethod@SpringBootTestUseMainMethod.ALWAYSUseMainMethod.WHEN_AVAILABLEALWAYSmainWHEN_AVAILABLEmain
例如,以下测试将调用 of 的方法以创建 .
如果 main 方法设置了其他配置文件,则这些配置文件将在启动时处于活动状态。mainMyApplicationApplicationContextApplicationContext
-
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() {
// ...
}
}
如果直接使用 (即,不是通过 ),则需要向 它注册 。
有关详细信息,请参阅 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 注入它们。@SpringBootTestargs
-
Java
-
Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {
@Test
fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
assertThat(args.optionNames).containsOnly("app.test")
assertThat(args.getOptionValues("app.test")).containsOnly("one")
}
}
使用 Mock 环境进行测试
默认情况下,不会启动服务器,而是设置用于测试 Web 端点的模拟环境。@SpringBootTest
借助 Spring MVC,我们可以使用 MockMvc 查询我们的 Web 端点。
有三种集成可用:
-
使用 Hamcrest 的常规
MockMvc。 -
MockMvcTester来包装并使用 AssertJ。MockMvc -
WebTestClient作为服务器插入以处理请求。MockMvc
以下示例展示了可用的集成:
-
Java
-
Kotlin
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If AssertJ is on the classpath, you can use MockMvcTester
@Test
void testWithMockMvcTester(@Autowired MockMvcTester mvc) {
assertThat(mvc.get().uri("/")).hasStatusOk().hasBodyTextEqualTo("Hello World");
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.assertj.MockMvcTester
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
fun testWithMockMvc(@Autowired mvc: MockMvcTester) {
assertThat(mvc.get().uri("/")).hasStatusOk()
.hasBodyTextEqualTo("Hello World")
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
如果您只想关注 Web 图层,而不开始 完整 ,请考虑改用 @WebMvcTest。ApplicationContext |
使用 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 层,因此无法直接使用 MockMvc 测试依赖于较低级别 servlet 容器行为的代码。 例如, Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。 这意味着,虽然您可以测试 MVC 层是否按预期引发和处理异常,但不能直接测试是否呈现了特定的自定义错误页面。 如果需要测试这些较低级别的问题,可以按照下一节所述启动完全运行的服务器。 |
如果您只想关注 Web 图层,而不开始 完整 ,请考虑改用 @WebMvcTest。ApplicationContext |
|
在模拟环境中进行测试通常比使用完整的 servlet 容器运行更快。 但是,由于模拟发生在 Spring MVC 层,因此无法直接使用 MockMvc 测试依赖于较低级别 servlet 容器行为的代码。 例如, Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。 这意味着,虽然您可以测试 MVC 层是否按预期引发和处理异常,但不能直接测试是否呈现了特定的自定义错误页面。 如果需要测试这些较低级别的问题,可以按照下一节所述启动完全运行的服务器。 |