https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#features.testing
Spring Boot 提供了一系列实用程序和注解来帮助测试你的应用程序。测试支持由两个模块提供:spring-boot-test
包含核心项目,而 spring-boot-test-autoconfigure
支持测试的自动配置。
大多数开发人员都使用 spring-boot-starter-test
“Starter”,它导入 Spring Boot 测试模块以及 JUnit Jupiter、AssertJ、Hamcrest 和其它一些有用的库。
提示:
如果你有使用 JUnit 4 的测试,那么可以使用 JUnit 5 的 vintage 引擎来运行它们。要使用 vintage 引擎,请添加 junit-vintage-engine
的依赖项,如下所示:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
hamcrest-core
被排除在外,而选择了 spring-boot-starter-test
中的 org.hamcrest:hamcrest
。这句话的意思是,在 Spring Boot 项目中,hamcrest-core
这个库没有被使用,而是选择了 spring-boot-starter-test
提供的 org.hamcrest:hamcrest
库。
测试范围依赖(Test Scope Dependencies)
spring-boot-starter-test
“Starter”(在测试范围内)包含以下提供的库:
- JUnit 5:Java 应用程序单元测试的事实标准。
- Spring Test 和 Spring Boot Test:为 Spring Boot 应用程序提供实用程序和集成测试支持。
- AssertJ:一个流畅的断言库。
- Hamcrest:一个匹配器对象库(也称为约束或谓词(constraints or predicates))。
- Mockito:一个 Java 模拟框架。
- JSONassert:一个用于 JSON 的断言库。
- JsonPath:JSON 的 XPath。
- Awaitility:一个用于测试异步系统的库。
在编写测试时,通常这些常用库很有用。如果这些库不符合你的需求,那么你可以添加你自己的其它测试依赖项。
测试Spring 应用
依赖注入的一个主要优点是它可以使你的代码更容易进行单元测试。你甚至可以在不涉及 Spring 的情况下使用 new 运算符实例化对象。还可以使用mock
对象而不是实际依赖项。
通常,你需要超越单元测试并开始进行集成测试(使用 Spring ApplicationContext
)。能够在不部署应用程序或需要连接到其它基础设施的情况下进行集成测试是非常有用的。
Spring Framework 为此类集成测试提供了一个专用的测试模块。你可以直接声明对 org.springframework:spring-test
的依赖,或者使用 spring-boot-starter-test
“Starter” 以传递方式将其拉入。
测试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
不会启动服务器。你可以使用 @SpringBootTest
的 webEnvironment
属性来进一步调整测试的运行方式:
MOCK
(默认):加载一个WebApplicationContext
并提供模拟的 Web环境。在使用此注解时,不会启动嵌入式服务器。如果类路径上没有可用的 Web 环境,则此模式将透明地回退到创建常规的非 WebApplicationContext
。它可以与@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
一起使用,以进行基于模拟的 Web 应用程序测试。RANDOM_PORT
:加载一个WebServerApplicationContext
并提供一个真实的 Web 环境。嵌入式服务器会启动并监听一个随机端口。DEFINED_PORT
:加载一个WebServerApplicationContext
并提供一个真实的 Web 环境。嵌入式服务器会启动并监听一个已定义的端口(来自你的application.properties
文件)或默认端口8080
。NONE
:使用SpringApplication
加载ApplicationContext
,但不提供任何 Web 环境(模拟或其它方式)。
注意:如果你的测试是 @Transactional
,则默认情况下,它会在每个测试方法结束时回滚事务。但是,由于使用 RANDOM_PORT
或 DEFINED_PORT
的这种安排隐式地提供了真实的 servlet 环境,因此 HTTP 客户端和服务器在不同的线程中运行,因此也在不同的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。
注意:如果应用程序为管理服务器使用了不同的端口,那么使用 webEnvironment
= WebEnvironment.RANDOM_PORT
的 @SpringBootTest
还将在一个单独的随机端口上启动管理服务器。
检测web应用类型
如果Spring MVC可用,则会配置一个基于常规 MVC 的应用程序上下文。如果只有 Spring WebFlux,将检测到这一点并配置一个基于 WebFlux 的应用程序上下文。
如果两者都存在,Spring MVC 优先。如果你想在此场景中测试响应式 Web 应用程序,必须设置 spring.main.web-application-type
属性:
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
检测测试配置
如果你熟悉 Spring 测试框架,你可能已经习惯于使用 @ContextConfiguration(classes=…)
来指定要加载哪个 Spring @Configuration
。或者,你可能经常在测试中使用嵌套的 @Configuration
类。
在测试 Spring Boot 应用程序时,通常不需要这样做。每当你没有明确定义一个时,Spring Boot 的 @*Test
注解会自动搜索你的主要配置。
搜索算法从包含测试的包开始工作,直到找到一个用 @SpringBootApplication
或 @SpringBootConfiguration
注解的类。只要你以合理的方式组织代码,通常就可以找到主要配置。
注意:
如果你使用测试注解来测试应用程序的更具体的部分,则应避免添加特定于主方法应用程序类上特定区域的配置设置。
@SpringBootApplication
的底层组件扫描配置定义了排除过滤器,用于确保切片按预期工作。如果你在带有 @SpringBootApplication
注解的类上使用了明确的 @ComponentScan
指令,请注意这些过滤器将被禁用。如果你正在使用切片,则应该重新定义它们。
如果你想自定义主要配置,可以使用嵌套的 @TestConfiguration
类。与将用作应用程序主要配置的替代品的嵌套 @Configuration
类不同,嵌套的 @TestConfiguration
类将与应用程序的主要配置一起使用。
注意:Spring 的测试框架会在测试之间缓存应用程序上下文。因此,只要你的测试共享相同的配置(无论它是如何发现的),加载上下文的潜在耗时过程只会发生一次。
使用测试配置主方法(Using the Test Configuration Main Method)
通常,@SpringBootTest
发现的测试配置将是你的主要 @SpringBootApplication
。在大多数结构良好的应用程序中,此配置类还将包括用于启动应用程序的主方法。
例如,以下是典型 Spring Boot 应用程序中非常常见的代码模式:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
在上面的示例中,主方法除了委托给 SpringApplication.run
外,没有执行任何其它操作。但是,可以在调用 SpringApplication.run
之前应用自定义设置,从而拥有一个更复杂的主方法。
例如,以下是一个更改横幅模式并设置额外profile的应用程序:
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);
}
}
由于主方法中的自定义设置可能会影响结果 ApplicationContext
,因此你可能还希望使用主方法来创建测试中使用的 ApplicationContext
。默认情况下,@SpringBootTest
不会调用你的主方法,而是直接使用该类来创建 ApplicationContext
。
如果你想更改此行为,可以将 @SpringBootTest
的 useMainMethod
属性更改为 UseMainMethod.ALWAYS
或 UseMainMethod.WHEN_AVAILABLE
。当设置为 ALWAYS
时,如果找不到主方法,测试将失败。当设置为 WHEN_AVAILABLE
时,如果可用,将使用主方法,否则将使用标准加载机制。
例如,以下测试将调用 MyApplication
的主方法来创建 ApplicationContext
。如果主方法设置了额外的profile,那么当 ApplicationContext
启动时,这些profile将处于活动状态。
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.UseMainMethod;
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
void exampleTest() {
// ...
}
}
排除测试配置
如果你的应用程序使用组件扫描(例如,如果使用 @SpringBootApplication
或 @ComponentScan
),你可能会发现只为特定测试创建的顶级配置类意外地在所有地方都被选中。
@TestConfiguration
可以用于测试的内部类以自定义主要配置。@TestConfiguration
也可以用于顶层类。这样做表示不应通过扫描选择该类。然后,你可以在需要的地方显式导入该类,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
注意:如果你直接使用 @ComponentScan
(即不是通过 @SpringBootApplication
),则需要使用 TypeExcludeFilter
进行注册。
注意:导入的 @TestConfiguration
比内部类的 @TestConfiguration
处理得早,而导入的 @TestConfiguration
将在通过组件扫描找到的任何配置之前进行处理。一般来说,这种顺序差异没有明显的影响,但如果你依赖 bean 覆盖,则需要了解这一点。
使用应用参数(Using Application Arguments)
如果你的应用程序需要参数,可以使用 @SpringBootTest
的 args
属性将参数注入其中。
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");
}
}
使用模拟环境进行测试(Testing With a Mock Environment)
默认情况下,@SpringBootTest
不会启动服务器,而是为测试 Web 端点(endpoints)设置了一个模拟环境。
使用 Spring MVC,我们可以使用 MockMvc
或 WebTestClient
查询我们的 Web 端点,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
提示:如果只想关注 Web 层而不启动完整的 ApplicationContext
,请考虑使用 @WebMvcTest
。
对于 Spring WebFlux 端点,可以使用 WebTestClient
,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
提示:
在模拟环境中进行测试通常比使用完整的 servlet 容器运行更快。但是,由于模拟发生在 Spring MVC 层,因此依赖于较低级别 servlet 容器行为的代码无法直接使用 MockMvc 进行测试。
例如,Spring Boot 的错误处理基于 servlet 容器提供的“错误页面”支持。这意味着,尽管你可以测试 MVC 层是否按预期抛出并处理异常,但你无法直接测试是否渲染了特定的自定义错误页面。如果你需要测试这些较低级别的关注点,可以启动一个完全运行的服务器。
使用运行中的服务器进行测试
如果你需要启动一个完全运行的服务器,建议使用随机端口。如果使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
,则每次运行测试时都会随机选择一个可用端口。
@LocalServerPort
注解可用于将实际使用的端口注入到你的测试中。为了方便起见,需要向已启动的服务器发出 REST 调用的测试还可以 @Autowire
一个 WebTestClient
,它将相对链接解析为正在运行的服务器,并带有用于验证响应的专用 API,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
提示:通过使用 @AutoConfigureWebTestClient
注解测试类,还可以在不运行服务器的情况下使用模拟环境的 WebTestClient
。
此设置要求类路径上存在 spring-webflux
。如果你不能或不想添加 webflux
,Spring Boot 还提供了 TestRestTemplate
设施:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
定制WebTestClient
要自定义 WebTestClient
bean,请配置一个 WebTestClientBuilderCustomizer
bean。将使用此 bean 调用用于创建 WebTestClient
的 WebTestClient.Builder
。
使用JMX
由于测试上下文框架缓存上下文,因此默认情况下会禁用 JMX,以防止相同组件在相同域上注册。如果此类测试需要访问 MBeanServer
,请考虑将其标记为“dirty ”:
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");
// ...
}
}
使用度量(Using Metrics)
无论你的类路径如何,当使用 @SpringBootTest
时,除内存支持的以外,meter registries 将不会自动配置。
如果你需要在集成测试中将指标导出到不同的后端,请使用 @AutoConfigureObservability
注解它。
使用跟踪(Using Tracing)
无论你的类路径如何,当使用 @SpringBootTest
时,报告数据的跟踪组件将不会自动配置。
如果需要在集成测试中使用这些组件,请使用 @AutoConfigureObservability
注解测试。
如果你创建了自己的报告组件(例如自定义的 SpanExporter
或 SpanHandler
),并且不希望它们在测试中处于活动状态,可以使用 @ConditionalOnEnabledTracing
注解来禁用它们。
模拟和监视 Bean(Mocking and Spying Beans)
在运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,可能有一些在开发期间不可用的远程服务。当你想要模拟可能在真实环境中难以触发的故障时,模拟也很有用。
Spring Boot 包括一个 @MockBean
注解,可用于在 ApplicationContext
中定义 Mockito
模拟的 bean。可以使用此注解添加新的 bean 或替换单个现有的 bean 定义。该注解可以直接用于测试类、测试中的字段,或用于 @Configuration
类和字段。当在字段上使用此注解时,也会注入创建的模拟实例。在每个测试方法之后,模拟 bean 会自动重置。
注意:
如果你的测试使用了 Spring Boot 的测试注解之一(例如 @SpringBootTest
),则此功能将自动启用。要使用不同的配置来使用此功能,必须显式添加侦听器,如下所示:
import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@ContextConfiguration(classes = MyConfig.class)
@TestExecutionListeners({ MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class })
class MyTests {
// ...
}
以下示例使用模拟实现替换现有的 RemoteService
bean:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@SpringBootTest
class MyTests {
@Autowired
private Reverser reverser;
@MockBean
private RemoteService remoteService;
@Test
void exampleTest() {
given(this.remoteService.getValue()).willReturn("spring");
String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}
注意:无法使用 @MockBean
模拟在应用程序上下文刷新期间使用的 bean 的行为。当执行测试时,应用程序上下文刷新已完成,为时已晚,无法配置模拟行为。在这种情况下,建议使用 @Bean
方法创建和配置模拟。
此外,还可以使用 @SpyBean
将 一个Mockito spy
包装在任何现有 bean 上。
注意:尽管 Spring 的测试框架在测试之间缓存应用程序上下文,并重用具有相同配置的测试的上下文,但使用 @MockBean
或 @SpyBean
会影响缓存键,这可能会增加上下文的数量。
提示:如果使用 @SpyBean
监视具有 @Cacheable
方法的 bean,并且该方法通过名称引用参数,则你的应用程序必须使用 -parameters
进行编译。这将确保在 bean 被监视后,参数名称可用于缓存基础结构。
提示:当使用 @SpyBean
监视由 Spring 代理的 bean 时,在某些情况下,可能需要删除 Spring 的代理,例如在使用 given
或 when
设置预期时。使用 AopTestUtils.getTargetObject(yourProxiedSpy)
来执行此操作。
自动配置的测试(Auto-configured Tests)
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
模块 - Gson
- Jsonb
如果需要配置自动配置的元素,可以使用 @AutoConfigureJsonTesters
注解。
Spring Boot 包含基于 AssertJ 的帮助程序,可与 JSONAssert
和 JsonPath
库一起使用,以检查 JSON 是否符合预期。JacksonTester
、GsonTester
、JsonbTester
和 BasicJsonTester
类分别可用于 Jackson、Gson、Jsonb 和字符串。在使用 @JsonTest
时,测试类上的任何帮助程序字段都可以使用 @Autowired
进行注入。以下示例显示了一个针对 Jackson 的测试类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test
void serialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
}
@Test
void deserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
注意:JSON 帮助程序类也可以直接用于标准的单元测试。如果不使用 @JsonTest
,请在 @Before
方法中调用帮助程序的 initFields
方法。
如果使用 Spring Boot 基于 AssertJ 的帮助程序在给定 JSON 路径上的数字值上进行断言,则可能无法根据类型使用 isEqualTo
。相反,你可以使用 AssertJ 的 satisfies
来断言值匹配给定条件。例如,以下示例断言实际数字是一个浮点值,在 0.01 的偏移量内接近 0.15。
@Test
void someTest() throws Exception {
SomeObject value = new SomeObject(0.152f);
assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
自动配置的Spring MVC测试
要测试 Spring MVC 控制器是否按预期工作,请使用 @WebMvcTest
注解。@WebMvcTest
自动配置 Spring MVC 基础设施,并将扫描的 bean 限制为 @Controller
、@ControllerAdvice
、@JsonComponent
、Converter
、GenericConverter
、Filter
、HandlerInterceptor
、WebMvcConfigurer
、WebMvcRegistrations
和 HandlerMethodArgumentResolver
。当使用 @WebMvcTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
提示:如果需要注册额外的组件,例如 Jackson Module
,可以通过在测试中使用 @Import
导入其它配置类。
通常,@WebMvcTest
仅限于单个控制器,并与 @MockBean
结合使用,为所需的协作者提供模拟实现。
@WebMvcTest
还自动配置了 MockMvc
。Mock MVC 提供了一种强大的方式,可以在不需要启动完整的 HTTP 服务器的情况下快速测试 MVC 控制器。
提示:还可以通过在 MockMvc
上使用 @AutoConfigureMockMvc
注解,在非 @WebMvcTest
(如 @SpringBootTest
)中自动配置 MockMvc
。以下示例使用 MockMvc
:
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"));
}
}
提示:如果需要配置自动配置的元素(例如,何时应用 servlet 过滤器),则可以使用 @AutoConfigureMockMvc
注解中的属性。
如果你使用 HtmlUnit 和 Selenium,自动配置还会提供一个 HtmlUnit WebClient
bean 和/或一个 Selenium WebDriver
bean。以下示例使用 HtmlUnit:
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
注意:默认情况下,Spring Boot 将 WebDriver
bean 放入一个特殊的“scope”中,以确保在每个测试之后退出驱动程序并注入新实例。如果不希望这种行为,可以将 @Scope("singleton")
添加到你的 WebDriver
@Bean
定义中。
注意:Spring Boot 创建的 webDriver
scope 将替换具有相同名称的任何用户定义的作用域。如果你定义了自己的 webDriver
作用域,可能会发现在使用 @WebMvcTest
时它停止工作。
如果类路径中有 Spring Security,则 @WebMvcTest
还将扫描 WebSecurityConfigurer
bean。对于此类测试,你可以使用 Spring Security 的测试支持,而不是完全禁用安全性。
自动配置的Spring WebFlux测试
要测试 Spring WebFlux 控制器是否按预期工作,可以使用 @WebFluxTest
注解。@WebFluxTest
自动配置 Spring WebFlux 基础设施,并将扫描的 bean 限制为 @Controller
、@ControllerAdvice
、@JsonComponent
、Converter
、GenericConverter
、WebFilter
和 WebFluxConfigurer
。当使用 @WebFluxTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以使用@EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
提示:如果你需要注册额外的组件,例如 Jackson Module
,可以在测试中使用 @Import
来导入其它配置类。
通常,@WebFluxTest
仅限于单个控制器,并与 @MockBean
注解结合使用,为所需的协作者提供模拟实现。
@WebFluxTest
还自动配置了 WebTestClient
,它提供了一种强大的方式,可以在不需要启动完整的 HTTP 服务器的情况下快速测试 WebFlux 控制器。
提示:
还可以通过在 WebTestClient
上使用 @AutoConfigureWebTestClient
注解,在非 @WebFluxTest
(如 @SpringBootTest
)中自动配置 WebTestClient
。以下示例显示了一个同时使用 @WebFluxTest
和 WebTestClient
的类:
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");
}
}
此设置仅支持 WebFlux 应用程序,因为目前在模拟的 Web 应用程序中使用 WebTestClient
仅适用于 WebFlux
。
注意:@WebFluxTest
无法检测通过功能 Web 框架注册的路由。为了测试上下文中的 RouterFunction
bean,请考虑使用 @Import
或 @SpringBootTest
导入你自己的 RouterFunction
。
注意:@WebFluxTest
无法检测作为 SecurityWebFilterChain
类型 @Bean
注册的自定义安全配置。要将其包含在你的测试中,需要使用 @Import
或 @SpringBootTest
导入注册该 bean 的配置。
自动配置的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>
此测试模块提供了 GraphQlTester。有 GraphQlTester 变体,Spring Boot 将根据测试类型自动配置它们:
ExecutionGraphQlServiceTester
在服务器端执行测试,无需客户端或传输层- HttpGraphQlTester 使用连接到服务器的客户端进行测试,无论服务器是否处于活动状态
Spring Boot 使用 @GraphQlTest
注解帮助测试 Spring GraphQL 控制器。@GraphQlTest
自动配置了 Spring GraphQL
基础设施,而不涉及任何传输或服务器。这将扫描的 bean 限制为 @Controller
、RuntimeWiringConfigurer
、JsonComponent
、Converter
、GenericConverter
、DataFetcherExceptionResolver
、Instrumentation
和 GraphQlSourceBuilderCustomizer
。当使用 @GraphQlTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
通常,@GraphQlTest
仅限于一组控制器,并与 @MockBean
注解结合使用,为所需的协作者提供模拟实现。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.docs.web.graphql.runtimewiring.GreetingController;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.GraphQlTester;
@GraphQlTest(GreetingController.class)
class GreetingControllerTests {
@Autowired
private GraphQlTester graphQlTester;
@Test
void shouldGreetWithSpecificName() {
this.graphQlTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
@Test
void shouldGreetWithDefaultName() {
this.graphQlTester.document("{ greeting } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Spring!");
}
}
@SpringBootTest
测试是完整的集成测试,涉及整个应用程序。当使用随机或定义的端口时,将配置一个活动服务器,并自动贡献一个 HttpGraphQlTester
bean,以便你可以使用它来测试你的服务器。当配置 MOCK 环境时,你还可以通过将测试类注解为 @AutoConfigureHttpGraphQlTester
来请求一个 HttpGraphQlTester
bean:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.tester.AutoConfigureHttpGraphQlTester;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
void shouldGreetWithSpecificName(@Autowired HttpGraphQlTester graphQlTester) {
HttpGraphQlTester authenticatedTester = graphQlTester.mutate()
.webTestClient((client) -> client.defaultHeaders((headers) -> headers.setBasicAuth("admin", "ilovespring")))
.build();
authenticatedTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
}
自动配置的数据 Cassandra 测试
可以使用 @DataCassandraTest
来测试 Cassandra 应用程序。默认情况下,它会配置 CassandraTemplate
,扫描 @Table
类,并配置 Spring Data Cassandra 存储库。当使用 @DataCassandraTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}
自动配置的数据 Couchbase 测试
可以使用 @DataCouchbaseTest
来测试 Couchbase 应用程序。默认情况下,它会配置 CouchbaseTemplate
或 ReactiveCouchbaseTemplate
,扫描 @Document
类,并配置 Spring Data Couchbase 存储库。当使用 @DataCouchbaseTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;
@DataCouchbaseTest
class MyDataCouchbaseTests {
@Autowired
private SomeRepository repository;
// ...
}
自动配置的数据 Elasticsearch 测试
可以使用 @DataElasticsearchTest
来测试 Elasticsearch 应用程序。默认情况下,它会配置一个 ElasticsearchRestTemplate
,扫描 @Document
类,并配置 Spring Data Elasticsearch 存储库。当使用 @DataElasticsearchTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;
@DataElasticsearchTest
class MyDataElasticsearchTests {
@Autowired
private SomeRepository repository;
// ...
}
自动配置的数据 JPA 测试
可以使用 @DataJpaTest
注解来测试 JPA 应用程序。默认情况下,它会扫描 @Entity
类并配置 Spring Data JPA 存储库。如果类路径上存在嵌入式数据库,它也会进行配置。SQL 查询默认会被记录,这是通过将 spring.jpa.show-sql
属性设置为 true
实现的。你可以通过注解的 showSql
属性来禁用此功能。
当使用 @DataJpaTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。你可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
默认情况下,数据 JPA 测试是事务性的,并在每个测试结束时回滚。如果你不希望这样,可以按照以下方式禁用测试或整个类的事务管理:
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
数据 JPA 测试还可以注入一个 TestEntityManager
bean,它提供了一个专门为测试设计的替代标准 JPA EntityManager
。
提示:通过添加 @AutoConfigureTestEntityManager
,TestEntityManager
也可以被自动配置到任何基于 Spring 的测试类中。在这样做时,请确保你的测试在事务中运行,例如通过在测试类或方法上添加 @Transactional
。
如果需要,还可以使用 JdbcTemplate
。以下示例显示了 @DataJpaTest
注解的使用:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
class MyRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
void testExample() {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getEmployeeNumber()).isEqualTo("1234");
}
}
内存中的嵌入式数据库通常非常适合用于测试,因为它们速度快且不需要任何安装。然而,如果你更喜欢在真实数据库上运行测试,可以使用 @AutoConfigureTestDatabase
注解,如下所示:
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
自动配置的JDBC 测试
@JdbcTest
注解与 @DataJpaTest
类似,但它仅适用于只需要 DataSource 而不使用 Spring Data JDBC 的测试。默认情况下,它会配置一个内存中的嵌入式数据库和一个 JdbcTemplate
。当使用 @JdbcTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
bean。你可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
默认情况下,JDBC 测试是事务性的,并在每个测试结束时回滚。有如果不希望这样,可以按照以下方式禁用测试或整个类的事务管理:
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {
}
如果你更希望测试在真实数据库上运行,可以使用与 @DataJpaTest
相同的方式使用 @AutoConfigureTestDatabase
注解。
自动配置的 Data JDBC 测试
@DataJdbcTest
注解与 @JdbcTest
类似,但它用于测试使用 Spring Data JDBC 存储库的场景。默认情况下,它会配置一个内存中的嵌入式数据库、一个 JdbcTemplate
以及 Spring Data JDBC 存储库。当使用 @DataJdbcTest
注解时,仅扫描 AbstractJdbcConfiguration
的子类,而不会扫描常规的 @Component
和 @ConfigurationProperties
bean。你可以使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
默认情况下,Data JDBC 测试是事务性的,并在每个测试结束时回滚。如果你不希望这样,可以按照 JDBC 示例中所示的方式为单个测试或整个测试类禁用事务管理。
如果你希望测试在真实数据库上运行,你可以使用 @AutoConfigureTestDatabase
注解,就像在使用 @DataJpaTest
时那样。
自动配置的 Data R2DBC 测试
@DataR2dbcTest
注解与 @DataJdbcTest
类似,但它是用于测试使用 Spring Data R2DBC 存储库的场景。默认情况下,它会配置一个内存中的嵌入式数据库、一个 R2dbcEntityTemplate
和 Spring Data R2DBC 存储库。当使用 @DataR2dbcTest
注解时,它不会扫描常规的 @Component
和 @ConfigurationProperties
bean。可以通过使用 @EnableConfigurationProperties
来包含 @ConfigurationProperties
bean。
默认情况下,Data R2DBC 测试不是事务性的。
如果你希望测试在真实数据库上运行,而不是在默认的嵌入式数据库上运行,你可以使用 @AutoConfigureTestDatabase
注解,其使用方式与在 @DataJpaTest
中相同。
自动配置的jOOQ测试
你可以像使用@JdbcTest
一样使用@JooqTest
,但它是为与jOOQ相关的测试而设计的。由于jOOQ高度依赖于与数据库模式相对应的基于Java的模式,因此会使用现有的DataSource
。如果你想用内存数据库替换它,你可以使用@AutoConfigureTestDatabase
来覆盖这些设置。当使用@JooqTest
注解时,不会扫描常规的@Component
和@ConfigurationProperties
bean。可以使用@EnableConfigurationProperties
来包含@ConfigurationProperties
bean。
@JooqTest
用于配置 DSLContext
。下面的例子展示了如何使用 @JooqTest
注解:
import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
@JooqTest
class MyJooqTests {
@Autowired
private DSLContext dslContext;
// ...
}
默认情况下,jOOQ 测试是事务性的,并在每个测试结束时回滚。如果你不希望这样,你可以像 JDBC 示例中所示,为单个测试或整个测试类禁用事务管理。
自动配置的 MongoDB 数据测试
你可以使用@DataMongoTest
来测试MongoDB应用程序。默认情况下,它会配置一个MongoTemplate
,扫描@Document
类,并配置Spring Data MongoDB存储库。当使用@DataMongoTest
注解时,不会扫描常规的@Component
和@ConfigurationProperties
bean。可以使用@EnableConfigurationProperties
来包含@ConfigurationProperties
bean。
下面的类展示了如何使用@DataMongoTest
注解:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;
@DataMongoTest
class MyDataMongoDbTests {
@Autowired
private MongoTemplate mongoTemplate;
// ...
}
自动配置的Neo4j数据测试
你可以使用@DataNeo4jTest
来测试Neo4j应用程序。默认情况下,它会扫描@Node
类,并配置Spring Data Neo4j存储库。当使用@DataNeo4jTest
注解时,不会扫描常规的@Component
和@ConfigurationProperties
bean。你可以使用@EnableConfigurationProperties
来包含@ConfigurationProperties
bean。
以下示例展示了在Spring Boot中使用Neo4J测试的典型设置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
@DataNeo4jTest
class MyDataNeo4jTests {
@Autowired
private SomeRepository repository;
// ...
}
默认情况下,Neo4j数据测试是事务性的,并在每个测试结束时回滚。如果你不希望这样,你可以按照以下方式为单个测试或整个测试类禁用事务管理:
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
测试。
自动配置的Redis数据测试
你可以使用@DataRedisTest来
测试Redis应用程序。默认情况下,它会扫描@RedisHash
类,并配置Spring Data Redis存储库。当使用@DataRedisTest
注解时,不会扫描常规的@Component
和@ConfigurationProperties
bean。你可以使用@EnableConfigurationProperties
来包含@ConfigurationProperties
bean。
以下示例展示了@DataRedisTest注
解的使用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
自动配置的LDAP数据测试
你可以使用@DataLdapTest
来测试LDAP应用程序。默认情况下,它会配置一个内存中的嵌入式LDAP(如果可用),配置一个LdapTemplate
,扫描@Entry
类,并配置Spring Data LDAP存储库。当使用@DataLdapTest
注解时,不会扫描常规的@Component
和@ConfigurationProperties
bean。你可以使用@EnableConfigurationProperties
来包含@ConfigurationProperties
bean。
以下示例展示了@DataLdapTest注
解的使用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;
@DataLdapTest
class MyDataLdapTests {
@Autowired
private LdapTemplate ldapTemplate;
// ...
}
内存中的嵌入式LDAP通常对于测试来说效果很好,因为它速度快,不需要开发人员安装。然而,如果你更喜欢在真实的LDAP服务器上运行测试,你应该排除嵌入式LDAP的自动配置,如下所示:
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {
// ...
}
自动配置的REST客户端
你可以使用@RestClientTest
注解来测试REST客户端。默认情况下,它会自动配置Jackson、GSON和Jsonb的支持,配置RestTemplateBuilder
和RestClient.Builder
,并添加对MockRestServiceServer
的支持。当使用@RestClientTest
注解时,不会扫描常规的@Component
和@ConfigurationProperties
bean。你可以使用@EnableConfigurationProperties
来包含@ConfigurationProperties
bean。
在@RestClientTest
注解中,你应该使用value
或components
属性来指定你想要测试的特定bean。
当在被测试的bean中使用RestTemplateBuilder
,并且在构建RestTemplate
时调用了RestTemplateBuilder.rootUri(String rootUri)
方法,那么在MockRestServiceServer
的期望中应该省略根URI,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestTemplateServiceTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
当在被测试的bean中使用RestClient.Builder
,或者当使用RestTemplateBuilder
而没有调用rootUri(String rootURI)
方法时,必须在MockRestServiceServer
的期望中使用完整的URI,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientServiceTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("https://example.com/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
自动配置的Spring REST Docs测试
你可以使用@AutoConfigureRestDocs
注解在Mock MVC、REST Assured或WebTestClient的测试中使用Spring REST Docs。这将不再需要Spring REST Docs中的JUnit扩展。
@AutoConfigureRestDocs
注解可用于覆盖默认的输出目录(如果你使用Maven,则默认目录为target/generated-snippets
;如果你使用Gradle,则默认目录为build/generated-snippets
)。此外,它还可以用于配置出现在任何文档中URI的主机名、协议和端口号。
使用Mock MVC自动配置的Spring REST Docs测试
@AutoConfigureRestDocs
用于自定义MockMvc
bean,以便在测试基于servlet的Web应用程序时使用Spring REST Docs。你可以通过@Autowired
来注入它,并在你的测试中像通常使用Mock MVC和Spring REST Docs那样使用它,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Autowired
private MockMvc mvc;
@Test
void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}
}
如果你需要比@AutoConfigureRestDocs
注解提供的属性更多的Spring REST Docs配置控制,你可以使用RestDocsMockMvcConfigurationCustomizer
bean,如下所示:
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
如果你想要使用Spring REST Docs支持参数化的输出目录,你可以创建一个RestDocumentationResultHandler
bean。自动配置将总是使用此结果处理器调用alwaysDo
,从而使每个MockMvc
调用自动生成默认的片段。下面的例子展示了如何定义一个RestDocumentationResultHandler
:
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
使用 WebTestClient 自动配置的 Spring REST Docs 测试
@AutoConfigureRestDocs
也可以在与响应式 Web 应用程序一起使用 WebTestClient
时进行配置。你可以通过 @Autowired
注入它,并在你的测试中像通常使用 @WebFluxTest
和 Spring REST Docs 那样使用它,如下面的示例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void listUsers() {
this.webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(document("list-users"));
}
}
如果你需要对 Spring REST Docs 的配置拥有比 @AutoConfigureRestDocs
注解提供的属性更多的控制权,你可以使用 RestDocsWebTestClientConfigurationCustomizer
bean,如下面的示例所示:
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {
@Override
public void customize(WebTestClientRestDocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}
}
如果你想要利用 Spring REST Docs 对参数化输出目录的支持,你可以使用 WebTestClientBuilderCustomizer
来为每一个实体交换结果配置一个消费者。以下示例展示了如何定义这样一个 WebTestClientBuilderCustomizer
:
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
public WebTestClientBuilderCustomizer restDocumentation() {
return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
}
}
使用REST Assured自动配置Spring REST Docs测试
@AutoConfigureRestDocs
会在你的测试中创建一个预配置的 RequestSpecification
bean,这个 bean 用于使用 Spring REST Docs。你可以通过 @Autowired
注解来注入它,并在你的测试中像通常使用 REST Assured 和 Spring REST Docs 那样使用它,如下面的示例所示:
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Test
void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
given(documentationSpec)
.filter(document("list-users"))
.when()
.port(port)
.get("/")
.then().assertThat()
.statusCode(is(200));
}
}
如果你需要比 @AutoConfigureRestDocs
提供的属性更多的控制权来配置 Spring REST Docs,你可以使用 RestDocsRestAssuredConfigurationCustomizer
bean,如下面的示例所示:
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
自动配置的Spring Web服务测试
自动配置的Spring Web服务客户端测试
你可以使用@WebServiceClientTest
来测试使用Spring Web Services项目调用Web服务的应用程序。默认情况下,它会配置一个模拟的WebServiceServer
bean,并自动定制你的WebServiceTemplateBuilder
。
以下示例展示了@WebServiceClientTest
注解的使用:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest;
import org.springframework.ws.test.client.MockWebServiceServer;
import org.springframework.xml.transform.StringSource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.ws.test.client.RequestMatchers.payload;
import static org.springframework.ws.test.client.ResponseCreators.withPayload;
@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {
@Autowired
private MockWebServiceServer server;
@Autowired
private SomeWebService someWebService;
@Test
void mockServerCall() {
this.server
.expect(payload(new StringSource("<request/>")))
.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}
}
自动配置的Spring Web服务服务器测试
你可以使用@WebServiceServerTest
来测试使用Spring Web Services项目实现Web服务的应用程序。默认情况下,它会配置一个MockWebServiceClient
bean,该bean可用于调用你的Web服务终端节点。
以下示例展示了@WebServiceServerTest
注解的使用:
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>")));
}
}
额外的自动配置和切片
每个切片提供一个或多个 @AutoConfigure...
注解,这些注解明确地定义了应该作为切片一部分包含的自动配置。可以通过创建自定义的 @AutoConfigure...
注解或向测试中添加 @ImportAutoConfiguration
来在每次测试时添加额外的自动配置,如下所示的示例所示:
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {
}
注意:确保不要使用普通的@Import
注解来导入自动配置,因为Spring Boot会以特定的方式处理它们。
作为替代方案,可以通过将它们注册到存储在META-INF/spring
文件夹中的文件中,为任何使用切片注解的情况添加额外的自动配置,如下所示:
META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports
com.example.IntegrationAutoConfiguration
在这个例子中,com.example.IntegrationAutoConfiguration
会在每个使用 @JdbcTest
注解的测试中被启用。
提示:你可以在此文件中使用 #
来添加注释。
提示:只要一个切片或 @AutoConfigure...
注解使用了 @ImportAutoConfiguration
元注解,就可以通过这种方式进行自定义。
用户配置和切片
如果你以一种合理的方式组织你的代码,那么你的@SpringBootApplication
类将默认被用作你的测试配置。
因此,重要的是不要在应用程序的主类中混入那些特定于其功能某个领域的配置设置。
假设你正在使用Spring Data MongoDB,并且依赖于它的自动配置,同时你也启用了审计功能。你可以按照以下方式定义你的@SpringBootApplication
:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {
// ...
}
因为这个类是测试的源配置,任何切片测试都会尝试启用Mongo审计,这绝对不是你想做的。一个推荐的做法是将这个特定区域的配置移到一个与你的应用程序同级的单独的@Configuration
类中,如下所示:
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {
// ...
}
注意:根据你的应用程序的复杂性,你可能只有一个用于自定义的@Configuration
类,或者每个领域区域都有一个类。后一种方法允许你在必要时使用@Import
注释在你的测试之一中启用它。
测试切片会排除@Configuration
类的扫描。例如,对于@WebMvcTest
,以下配置将不会将给定的WebMvcConfigurer
bean包含在由测试切片加载的应用程序中:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {
@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
// ...
};
}
}
然而,下面的配置将导致自定义的WebMvcConfigurer
被测试切片加载。
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
// ...
}
另一个令人困惑的来源是类路径扫描。假设你以一种合理的方式组织了你的代码,但你需要扫描一个额外的包。你的应用程序可能类似于以下代码:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
这样做实际上会覆盖默认的组件扫描指令,其副作用是无论你选择哪个切片,都会扫描这两个包。例如,@DataJpaTest
似乎会突然扫描你的应用程序的组件和用户配置。再次强调,将自定义指令移动到单独的类中是解决此问题的好方法。
提示:如果这不是你的选择,你可以在你的测试层次结构中的某个地方创建一个@SpringBootConfiguration
,以便它被使用。另外,你可以为你的测试指定一个源,这会禁用查找默认源的行为。
使用Spock测试Spring Boot应用程序
可以使用Spock 2.2或更高版本来测试Spring Boot应用程序。为此,需要将Spock的spock-spring
模块的-groovy-4.0
版本依赖项添加到应用程序的构建中。spock-spring
将Spring的测试框架集成到Spock中。
Testcontainers
Testcontainers
库提供了一种管理在Docker容器内运行的服务的方法。它与JUnit集成,使你可以编写一个测试类,在任何测试运行之前启动一个容器。Testcontainers
对于编写与真实后端服务(如MySQL、MongoDB、Cassandra等)进行交互的集成测试特别有用。
Testcontainers可以在Spring Boot测试中使用,如下所示:
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
这将在任何测试运行之前启动一个运行Neo4j的Docker容器(如果Docker在本地运行)。在大多数情况下,你需要配置应用程序以连接到容器中运行的服务。
服务连接
服务连接是对任何远程服务的连接。Spring Boot的自动配置可以消费服务连接的详细信息,并使用它们建立与远程服务的连接。在这样做时,连接详细信息将优先于任何与连接相关的配置属性。
当使用Testcontainers时,可以通过在测试类中的容器字段上添加注解来自动为在容器中运行的服务创建连接详细信息。
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
@ServiceConnection
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
由于@ServiceConnection
注解,上述配置允许应用程序中的Neo4j相关bean与Testcontainers管理的Docker容器内运行的Neo4j进行通信。这是通过自动定义Neo4jConnectionDetails
bean来完成的,该bean随后被Neo4j自动配置使用,覆盖任何与连接相关的配置属性。
注意:要使用Testcontainers
中的服务连接,需要将spring-boot-testcontainers
模块作为测试依赖项添加到项目中。
服务连接注解由通过spring.factories
注册的ContainerConnectionDetailsFactory
类进行处理。ContainerConnectionDetailsFactory
可以根据特定的Container
子类或Docker镜像名称创建ConnectionDetails
bean。
spring-boot-testcontainers
jar中提供了以下服务连接工厂:
提示:
默认情况下,将为给定的Container创建所有适用的连接详细信息bean。例如,PostgreSQLContainer
将创建JdbcConnectionDetails
和R2dbcConnectionDetails
。
如果只想创建适用类型的子集,可以使用@ServiceConnection
的type
属性。
默认情况下,使用Container.getDockerImageName()
来获取用于查找连接详细信息的名称。只要Spring Boot能够获取Container
的实例,这就可以工作,就像上面的示例中使用的静态字段一样。
如果你使用的是@Bean
方法,Spring Boot不会调用bean方法来获取Docker镜像名称,因为这会导致急切初始化问题。相反,使用bean方法的返回类型来确定应使用哪个连接详细信息。只要你使用类型化的容器(例如Neo4jContainer
或RabbitMQContainer
),这就可以工作。但是,如果你使用GenericContainer
(例如Redis),如下例所示,则此方法将不起作用:
import org.testcontainers.containers.GenericContainer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
@TestConfiguration(proxyBeanMethods = false)
public class MyRedisConfiguration {
@Bean
@ServiceConnection(name = "redis")
public GenericContainer<?> redisContainer() {
return new GenericContainer<>("redis:7");
}
}
Spring Boot无法从GenericContainer
中确定使用了哪个容器镜像,因此必须使用@ServiceConnection
中的name
属性来提供该提示。
还可以使用@ServiceConnection
的name
属性来覆盖将使用哪个连接详细信息,例如在使用自定义镜像时。如果使用Docker镜像registry.mycompany.com/mirror/myredis
,则可以使用@ServiceConnection(name="redis")
来确保创建了RedisConnectionDetails
。
动态属性
与服务连接相比,稍微冗长但更灵活的替代方案是@DynamicPropertySource
。静态的@DynamicPropertySource方
法允许将动态属性值添加到Spring Environment
中。
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
}
上述配置允许应用程序中的Neo4j相关bean与Testcontainers管理的Docker容器内运行的Neo4j进行通信。
测试实用程序
一些在测试你的应用程序时通常很有用的测试实用程序类被打包为spring-boot
的一部分。
ConfigDataApplicationContextInitializer
ConfigDataApplicationContextInitializer
是一个ApplicationContextInitializer
,你可以将其应用于测试以加载Spring Boot application.properties
文件。当你不需要@SpringBootTest
提供的完整功能集时,可以使用它,如下所示:
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}
注意:单独使用ConfigDataApplicationContextInitializer
不支持@Value("${…}")
注入。它的唯一工作就是确保application.properties
文件被加载到Spring的Environment
中。对于@Value
支持,你需要另外配置一个PropertySourcesPlaceholderConfigurer
,或者使用@SpringBootTest
,后者会为你自动配置一个。
TestPropertyValues
TestPropertyValues
允许你快速将属性添加到ConfigurableEnvironment
或ConfigurableApplicationContext
中。你可以使用key=value
字符串调用它,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
class MyEnvironmentTests {
@Test
void testPropertySources() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
assertThat(environment.getProperty("name")).isEqualTo("Boot");
}
}
CapturedOutput
CapturedOutput
是一个JUnit Extension
,你可以使用它来捕获System.out
和System.err
输出。要使用它,请添加@ExtendWith(OutputCaptureExtension.class)
,并将CapturedOutput
注入到测试类构造函数或测试方法的参数中,如下所示:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
TestRestTemplate
TestRestTemplate
是Spring的RestTemplate
的便捷替代方案,适用于集成测试。你可以获得一个普通的模板,或者发送基本HTTP身份验证(带有用户名和密码)的模板。无论是哪种情况,模板都具有容错性。这意味着它在4xx
和5xx
错误上不抛出异常,而是以测试友好的方式进行处理。相反,可以通过返回的ResponseEntity
及其状态码来检测此类错误。
提示:Spring Framework 5.0提供了一个新的WebTestClient
,适用于WebFlux集成测试和WebFlux和MVC的端到端测试。与TestRestTemplate
不同,它提供了流畅的API用于断言。
建议使用(但并非必须)Apache HTTP客户端(版本5.1或更高版本)。如果你的类路径中有该客户端,则TestRestTemplate
会通过适当地配置客户端来响应。如果你使用Apache的HTTP客户端,则会启用一些额外的测试友好功能:
- 不遵循重定向(因此你可以断言响应位置)。
- 忽略Cookie(因此模板是无状态的)。
可以在集成测试中直接实例化TestRestTemplate
,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
class MyTests {
private final TestRestTemplate template = new TestRestTemplate();
@Test
void testRequest() {
ResponseEntity<String> headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}
}
另外,如果你使用@SpringBootTest
注解并指定WebEnvironment.RANDOM_PORT
或WebEnvironment.DEFINED_PORT
,则可以注入一个已完全配置的TestRestTemplate
并开始使用它。如果需要,可以通过RestTemplateBuilder
bean应用其它自定义设置。任何未指定主机和端口的URL都会自动连接到嵌入式服务器,如下所示:
import java.time.Duration;
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.context.TestConfiguration;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration(proxyBeanMethods = false)
static class RestTemplateBuilderConfiguration {
@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}