Appearance
Spring Boot:核心特性 - Part2
1. 测试
Spring Boot 提供了许多实用工具和注解,以帮助测试您的应用程序。测试支持由两个模块提供:spring-boot-test 包含核心项目,spring-boot-test-autoconfigure 支持测试的自动配置。
大多数开发人员使用 spring-boot-starter-test “Starter”,它导入了两个 Spring Boot 测试模块以及 JUnit Jupiter、AssertJ、Hamcrest 和其他一些有用的库。
Note
如果您有使用 JUnit 4 的测试,JUnit 5 的 vintage 引擎可以用来运行它们。要使用 vintage 引擎,请添加对
junit-vintage-engine的依赖,如下例所示:XML<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 被排除,以支持 org.hamcrest:hamcrest,它是 spring-boot-starter-test 的一部分。
1.1. 测试范围依赖
spring-boot-starter-test “Starter”(在 test 范围内)包含以下提供的库:
- JUnit 5:Java 应用程序单元测试的事实标准;
- Spring Test 和 Spring Boot Test:Spring Boot 应用程序的实用工具和集成测试支持;
- AssertJ:一个流式的断言库;
- Hamcrest:一个匹配器对象库(也称为约束或谓词);
- Mockito:一个 Java 模拟框架;
- JSONassert:一个用于 JSON 的断言库;
- JsonPath:JSON 的 XPath;
我们通常发现这些常见库在编写测试时非常有用。如果这些库不适合您的需求,您可以添加其他测试依赖。
1.2. 测试 Spring 应用程序
依赖注入的一个主要优点是它应该使您的代码更容易进行单元测试。您可以使用 new 运算符实例化对象,而不需要涉及 Spring。您还可以使用 mock 对象而不是真正的依赖。
通常,您需要超越单元测试,开始进行集成测试(使用 Spring ApplicationContext)。能够在不需要部署应用程序或连接到其他基础设施的情况下进行集成测试是非常有用的。
Spring 框架包含一个专门的测试模块,用于此类集成测试。您可以直接声明对 org.springframework:spring-test 的依赖,或者使用 spring-boot-starter-test “Starter” 来传递引入它。
如果您之前没有使用过 spring-test 模块,您应该从阅读 Spring 框架参考文档的相关部分开始。
1.3. 测试 Spring Boot 应用程序
Spring Boot 应用程序是一个 Spring ApplicationContext,因此除了您通常对普通 Spring 上下文所做的事情之外,没有什么特别的事情需要做。
Tip:外部属性、日志记录和 Spring Boot 的其他功能仅在使用
SpringApplication创建上下文时默认安装在上下文中。
Spring Boot 提供了一个 @SpringBootTest 注解,可以作为标准 spring-test 的 @ContextConfiguration 注解的替代品,当您需要 Spring Boot 功能时。该注解通过通过 SpringApplication 创建测试中使用的 ApplicationContext 来工作。除了 @SpringBootTest,还提供了许多其他注解,用于测试应用程序的更具体的切片。
Note:如果您使用的是 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 环境(模拟或其他)。
Tip:如果您的测试是
@Transactional,则默认情况下它会在每个测试方法结束时回滚事务。但是,由于使用RANDOM_PORT或DEFINED_PORT隐式提供了一个真实的 servlet 环境,HTTP 客户端和服务器在不同的线程中运行,因此在不同的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。
Tip:
@SpringBootTest与webEnvironment = WebEnvironment.RANDOM_PORT还会在应用程序使用不同端口的管理服务器上启动管理服务器。
1.3.1. 检测 Web 应用程序类型
如果 Spring MVC 可用,则配置一个常规的基于 MVC 的应用程序上下文。如果您只有 Spring WebFlux,我们会检测到它并配置一个基于 WebFlux 的应用程序上下文。
如果两者都存在,Spring MVC 优先。如果您想在这种情况下测试一个响应式 web 应用程序,您必须设置 spring.main.web-application-type 属性:
Java
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
1.3.2. 检测测试配置
如果您熟悉 Spring 测试框架,您可能习惯于使用 @ContextConfiguration(classes=…) 来指定要加载哪个 Spring @Configuration。或者,您可能经常在测试中使用嵌套的 @Configuration 类。
在测试 Spring Boot 应用程序时,这通常是不需要的。Spring Boot 的 @*Test 注解会自动搜索您的主配置,只要您没有明确定义一个。
搜索算法从包含测试的包开始,直到找到一个带有 @SpringBootApplication 或 @SpringBootConfiguration 注解的类。只要您合理地结构化代码,通常会找到您的主配置。
Tip
如果您使用测试注解来测试应用程序的更具体的切片,则应避免在主方法的应用程序类上添加特定于某个区域的配置设置。
@SpringBootApplication注解的底层组件扫描配置定义了排除过滤器,用于确保切片按预期工作。如果您在带有@SpringBootApplication注解的类上使用显式的@ComponentScan指令,请注意这些过滤器将被禁用。如果您使用切片,请再次定义它们。
如果您想自定义主配置,可以使用嵌套的 @TestConfiguration 类。与嵌套的 @Configuration 类不同,后者将用于代替应用程序的主配置,嵌套的 @TestConfiguration 类将用于添加到应用程序的主配置。
Tip:Spring 的测试框架在测试之间缓存应用程序上下文。因此,只要您的测试共享相同的配置(无论如何发现它),耗时的加载上下文的过程只发生一次。
1.3.3. 排除测试配置
如果您的应用程序使用组件扫描(例如,如果您使用 @SpringBootApplication 或 @ComponentScan),您可能会发现您仅为特定测试创建的顶级配置类意外地在所有地方被捕获。
如我们之前所见,@TestConfiguration 可以用于测试类的内部类来自定义主配置。@TestConfiguration 也可以用于顶级类。这样做表明该类不应被扫描捕获。然后可以在需要的地方显式导入该类,如下例所示:
Java
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() {
// ...
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Tip:如果您直接使用
@ComponentScan(即不通过@SpringBootApplication),您需要使用TypeExcludeFilter与之注册。请参阅 Javadoc 以获取详细信息。
Tip:导入的
@TestConfiguration比内部类的@TestConfiguration更早处理,导入的@TestConfiguration将在通过组件扫描找到的任何配置之前处理。一般来说,这种顺序差异没有明显的影响,但如果您依赖 bean 覆盖,这是需要注意的。
1.3.4. 使用应用程序参数
如果您的应用程序期望参数,您可以使用 @SpringBootTest 的 args 属性注入它们。
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.3.5. 使用模拟环境进行测试
默认情况下,@SpringBootTest 不会启动服务器,而是设置一个模拟环境来测试 web 端点。
使用 Spring MVC,我们可以使用 MockMvc 或 WebTestClient 查询我们的 web 端点,如下例所示:
Java
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"));
}
// 如果 Spring WebFlux 在类路径上,您可以使用 WebTestClient 驱动 MVC 测试
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Note:如果您只想关注 web 层并且不想启动完整的
ApplicationContext,请考虑使用@WebMvcTest代替。
使用 Spring WebFlux 端点,您可以使用 WebTestClient,如下例所示:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Note
在模拟环境中进行测试通常比在完整的 servlet 容器中运行更快。但是,由于模拟发生在 Spring MVC 层,依赖于低级 servlet 容器行为的代码无法直接使用 MockMvc 进行测试。
例如,Spring Boot 的错误处理基于 servlet 容器提供的 “错误页面” 支持。这意味着,虽然您可以测试 MVC 层是否抛出并处理异常,但无法直接测试是否呈现特定的自定义错误页面。如果您需要测试这些低级关注点,可以按照下一节中的描述启动一个完全运行的服务器。
1.3.6. 使用正在运行的服务器进行测试
如果您需要启动一个完全运行的服务器,我们建议您使用随机端口。如果您使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT),每次运行测试时都会选择一个可用的端口。
@LocalServerPort 注解可以用于将实际使用的端口注入到您的测试中。为了方便,需要对启动的服务器进行 REST 调用的测试可以额外 @Autowire 一个 WebTestClient,它解析相对链接到运行的服务器,并带有一个专用 API 来验证响应,如下例所示:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Note:
WebTestClient也可以与模拟环境一起使用,消除了对运行服务器的需要,方法是使用@AutoConfigureWebTestClient注解您的测试类。
此设置需要在类路径上有 spring-webflux。如果您不能或不想添加 webflux,Spring Boot 还提供了一个 TestRestTemplate 设施:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.3.7. 自定义 WebTestClient
要自定义 WebTestClient bean,请配置一个 WebTestClientBuilderCustomizer bean。任何这样的 bean 都会使用 WebTestClient.Builder 调用,该构建器用于创建 WebTestClient。
1.3.8. 使用 JMX
由于测试上下文框架缓存上下文,JMX 默认情况下被禁用,以防止相同域中的相同组件注册。如果这样的测试需要访问 MBeanServer,请将其标记为脏:
Java
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");
// ...
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1.3.9. 使用指标
无论您的类路径如何,除了内存支持的计量器注册表之外,使用 @SpringBootTest 时不会自动配置计量器注册表。
如果您需要在集成测试的一部分中将指标导出到不同的后端,请使用 @AutoConfigureMetrics 对其进行注解。
1.3.10. 模拟和监视 Beans
在运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,您可能有一个外观,它覆盖了某个远程服务,该服务在开发过程中不可用。模拟还可以在真实环境中难以触发故障时非常有用。
Spring Boot 包括一个 @MockBean 注解,可以用来在您的 ApplicationContext 中定义一个 Mockito 模拟 bean。您可以使用该注解来添加新的 bean 或替换单个现有的 bean 定义。该注解可以直接用于测试类、测试类中的字段或 @Configuration 类和字段。当用于字段时,创建的模拟实例也会被注入。模拟 bean 在每个测试方法之后自动重置。
Tip
如果您的测试使用 Spring Boot 的测试注解(例如
@SpringBootTest),则此功能会自动启用。要与不同的安排一起使用此功能,必须显式添加侦听器,如下例所示:Javaimport 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 { // ... }1
2
3
4
5
6
7
8
9
10
11
12
以下示例替换了现有的 RemoteService bean 的模拟实现:
Java
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(); // 调用注入的 RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Tip:
@MockBean无法用于模拟在应用程序上下文刷新期间执行的 bean 的行为。在测试执行时,应用程序上下文刷新已完成,此时配置模拟行为已经太晚。我们建议在这种情况下使用@Bean方法来创建和配置模拟。
此外,您可以使用 @SpyBean 来包装任何现有的 bean 与 Mockito spy。请参阅 Javadoc 以获取完整详细信息。
Tip:CGLib 代理(例如为作用域 bean 创建的代理)声明代理方法为
final。这阻止了 Mockito 在其默认配置中正常工作,因为它无法模拟或监视final方法。如果您想模拟或监视这样的 bean,请通过将org.mockito:mockito-inline添加到应用程序的测试依赖项中来配置 Mockito 使用其内联模拟制造器。这允许 Mockito 模拟和监视final方法。
Tip:虽然 Spring 的测试框架在测试之间缓存应用程序上下文并重用相同配置的上下文,但使用
@MockBean或@SpyBean会影响缓存键,这可能会增加上下文的数量。
Note:如果您使用
@SpyBean来监视带有@Cacheable方法的 bean,这些方法引用参数名称,则您的应用程序必须使用-parameters进行编译。这确保了在 bean 被监视后,参数名称可用于缓存基础设施。
Note:当您使用
@SpyBean来监视由 Spring 代理的 bean 时,您可能需要在某些情况下删除 Spring 的代理,例如在使用given或when设置期望时。使用AopTestUtils.getTargetObject(yourProxiedSpy)来做到这一点。
1.3.11. 自动配置测试
Spring Boot 的自动配置系统对应用程序非常有效,但有时对测试来说可能有点过头。通常,加载应用程序的某个 “切片” 所需的配置部分有助于测试。例如,您可能希望测试 Spring MVC 控制器是否正确映射 URL,并且不希望在这些测试中涉及数据库调用,或者您可能希望测试 JPA 实体,并且在这些测试运行时不关心 Web 层。
spring-boot-test-autoconfigure 模块包含许多注解,可以用来自动配置这些 “切片”。每个注解的工作方式类似,提供一个 @…Test 注解,该注解加载 ApplicationContext 和一个或多个 @AutoConfigure… 注解,可以用来自定义自动配置设置。
Tip:每个切片都限制组件扫描到适当的组件,并加载非常有限的一组自动配置类。如果需要排除其中一个,大多数
@…Test注解提供了excludeAutoConfiguration属性。或者,您可以使用@ImportAutoConfiguration#exclude。
Tip:通过使用几个
@…Test注解在一个测试中包含多个 “切片” 是不支持的。如果您需要多个 “切片”,请选择其中一个@…Test注解,并手动包含其他 “切片” 的@AutoConfigure…注解。
Note:也可以将
@AutoConfigure…注解与标准的@SpringBootTest注解一起使用。如果您不需要 “切片” 应用程序,但希望使用一些自动配置的测试 bean,可以使用此组合。
1.3.12. 自动配置 JSON 测试
要测试对象的 JSON 序列化和反序列化是否按预期工作,可以使用 @JsonTest 注解。@JsonTest 自动配置可用的支持的 JSON 映射器,可以是以下库之一:
- Jackson
ObjectMapper、任何@JsonComponentbean 和任何 JacksonModule GsonJsonb
Note:
@JsonTest启用的自动配置列表可以在附录中找到。
如果需要配置自动配置的元素,可以使用 @AutoConfigureJsonTesters 注解。
Spring Boot 包含基于 AssertJ 的辅助程序,这些辅助程序与 JSONAssert 和 JsonPath 库一起工作,以检查 JSON 是否按预期显示。JacksonTester、GsonTester、JsonbTester 和 BasicJsonTester 类可以分别用于 Jackson、Gson、Jsonb 和字符串。使用 @JsonTest 时,测试类上的任何辅助字段都可以 @Autowired。以下示例显示了一个用于 Jackson 的测试类:
Java
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");
// 断言与测试中的 `.json` 文件相同的包
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// 或者使用基于 JSON 路径的断言
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Tip:JSON 辅助类也可以直接在标准单元测试中使用。要做到这一点,请在
@Before方法中调用辅助程序的initFields方法,如果不使用@JsonTest。
如果使用 Spring Boot 的基于 AssertJ 的辅助程序来断言给定 JSON 路径处的数值,可能无法使用 isEqualTo,具体取决于类型。相反,可以使用 AssertJ 的 satisfies 来断言值匹配给定条件。例如,以下示例断言实际数字是接近 0.15 的浮点值,偏差为 0.01。
Java
@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)));
}1
2
3
4
5
6
2
3
4
5
6
1.3.13. 自动配置 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。
Note:
@WebMvcTest启用的自动配置设置列表可以在附录中找到。
Note:如果需要注册额外的组件(例如 Jackson
Module),可以使用@Import在测试中导入其他配置类。
通常,@WebMvcTest 限制为单个控制器,并与 @MockBean 结合使用,以提供所需协作者的模拟实现。
@WebMvcTest 还自动配置 MockMvc。Mock MVC 提供了一种强大的方法,可以快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。
Note:也可以在非
@WebMvcTest(例如@SpringBootTest)中自动配置MockMvc,方法是使用@AutoConfigureMockMvc对其进行注解。以下示例使用MockMvc:
Java
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"));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Note:如果需要配置自动配置的元素(例如,应用 servlet 过滤器时),可以使用
@AutoConfigureMockMvc注解中的属性。
如果使用 HtmlUnit 和 Selenium,自动配置还提供了一个 HtmlUnit WebClient bean 和/或一个 Selenium WebDriver bean。以下示例使用 HtmlUnit:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Tip:默认情况下,Spring Boot 将
WebDriverbean 放在一个特殊的 “作用域” 中,以确保驱动程序在每次测试后退出,并且注入一个新实例。如果不希望这种行为,可以在WebDriver@Bean定义中添加@Scope("singleton")。
Warning:Spring Boot 创建的
webDriver作用域将替换同名的用户定义作用域。如果您定义了自己的webDriver作用域,您可能会发现在使用@WebMvcTest时它停止工作。
如果类路径上有 Spring Security,@WebMvcTest 还会扫描 WebSecurityConfigurer bean。不要完全禁用此类测试的安全性,可以使用 Spring Security 的测试支持。有关如何使用 Spring Security 的 MockMvc 支持的更多详细信息,请参阅此使用 Spring Security 进行测试操作指南部分。
Note:有时编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您运行使用实际服务器的完整端到端测试。
1.3.14. 自动配置 Spring WebFlux 测试
要测试 Spring WebFlux 控制器是否按预期工作,可以使用 @WebFluxTest 注解。@WebFluxTest 自动配置 Spring WebFlux 基础设施,并将扫描的 bean 限制为 @Controller、@ControllerAdvice、@JsonComponent、Converter、GenericConverter、WebFilter 和 WebFluxConfigurer。当使用 @WebFluxTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@WebFluxTest启用的自动配置列表可以在附录中找到。
Note:如果需要注册额外的组件(例如 Jackson
Module),可以使用@Import在测试中导入其他配置类。
通常,@WebFluxTest 限制为单个控制器,并与 @MockBean 注解结合使用,以提供所需协作者的模拟实现。
@WebFluxTest 还自动配置 WebTestClient,它提供了一种强大的方法,可以快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。
Note:也可以在非
@WebFluxTest(例如@SpringBootTest)中自动配置WebTestClient,方法是使用@AutoConfigureWebTestClient对其进行注解。以下示例显示了一个使用@WebFluxTest和WebTestClient的类:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Note:此设置仅由 WebFlux 应用程序支持,因为使用
WebTestClient在模拟的 Web 应用程序中仅适用于 WebFlux。
Tip:
@WebFluxTest无法检测通过功能性 Web 框架注册的路由。要在上下文中测试RouterFunctionbean,请考虑使用@Import自行导入您的RouterFunction,或者使用@SpringBootTest。
Tip:
@WebFluxTest无法检测作为SecurityWebFilterChain类型的@Bean注册的自定义安全配置。要在测试中包含该配置,您需要使用@Import导入注册 bean 的配置,或者使用@SpringBootTest。
Note:有时编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您运行使用实际服务器的完整端到端测试。
1.3.15. 自动配置 Spring GraphQL 测试
Spring GraphQL 提供了一个专用的测试支持模块;您需要将其添加到项目中:
XML
<dependencies>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 除非已经在编译范围内 -->
<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")
// 除非已经在实现配置中
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
}此测试模块附带 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。
Note:
@GraphQlTest启用的自动配置列表可以在附录中找到。
Note:如果需要注册额外的组件(例如 Jackson
Module),可以使用@Import在测试中导入其他配置类。
通常,@GraphQlTest 限制为一组控制器,并与 @MockBean 注解结合使用,以提供所需协作者的模拟实现。
Java
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!");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@SpringBootTest 测试是完整的集成测试,涉及整个应用程序。使用随机或定义的端口时,会配置一个实时服务器,并自动提供一个 HttpGraphQlTester bean,因此您可以使用它来测试服务器。配置 MOCK 环境时,也可以通过使用 @AutoConfigureHttpGraphQlTester 注解测试类来请求 HttpGraphQlTester bean:
Java
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!");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1.3.16. 自动配置 Data Cassandra 测试
可以使用 @DataCassandraTest 测试 Cassandra 应用程序。默认情况下,它配置一个 CassandraTemplate,扫描 @Table 类,并配置 Spring Data Cassandra 存储库。当使用 @DataCassandraTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Cassandra 与 Spring Boot 的更多信息,请参阅 “Cassandra”。)
Note:
@DataCassandraTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了在 Spring Boot 中使用 Cassandra 测试的典型设置:
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
1.3.17. 自动配置 Data Couchbase 测试
可以使用 @DataCouchbaseTest 测试 Couchbase 应用程序。默认情况下,它配置一个 CouchbaseTemplate 或 ReactiveCouchbaseTemplate,扫描 @Document 类,并配置 Spring Data Couchbase 存储库。当使用 @DataCouchbaseTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Couchbase 与 Spring Boot 的更多信息,请参阅 “Couchbase”,本章前面的内容。)
Note:
@DataCouchbaseTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了在 Spring Boot 中使用 Couchbase 测试的典型设置:
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.couchbase.DataCouchbaseTest;
@DataCouchbaseTest
class MyDataCouchbaseTests {
@Autowired
private SomeRepository repository;
// ...
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
1.3.18. 自动配置 Data Elasticsearch 测试
可以使用 @DataElasticsearchTest 测试 Elasticsearch 应用程序。默认情况下,它配置一个 ElasticsearchRestTemplate,扫描 @Document 类,并配置 Spring Data Elasticsearch 存储库。当使用 @DataElasticsearchTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Elasticsearch 与 Spring Boot 的更多信息,请参阅 “Elasticsearch”,本章前面的内容。)
Note:
@DataElasticsearchTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了在 Spring Boot 中使用 Elasticsearch 测试的典型设置:
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;
@DataElasticsearchTest
class MyDataElasticsearchTests {
@Autowired
private SomeRepository repository;
// ...
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
1.3.19. 自动配置 Data JPA 测试
您可以使用 @DataJpaTest 注解来测试 JPA 应用程序。默认情况下,它扫描 @Entity 类并配置 Spring Data JPA 存储库。如果类路径上有嵌入式数据库,它也会配置一个嵌入式数据库。SQL 查询通过将 spring.jpa.show-sql 属性设置为 true 来默认记录。可以使用注解的 showSql() 属性禁用此功能。
当使用 @DataJpaTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@DataJpaTest启用的自动配置设置列表可以在附录中找到。
默认情况下,Data JPA 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,可以按如下方式禁用事务管理:
Java
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 {
// ...
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Data JPA 测试还可以注入一个 TestEntityManager bean,它提供了标准 JPA EntityManager 的替代方案,专门设计用于测试。
Note:
TestEntityManager也可以自动配置到任何基于 Spring 的测试类中,方法是添加@AutoConfigureTestEntityManager。这样做时,请确保您的测试在事务中运行,例如通过在测试类或方法上添加@Transactional。
JdbcTemplate 也可用,如果您需要的话。以下示例显示了 @DataJpaTest 注解的使用:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
内存中的嵌入式数据库通常适用于测试,因为它们速度快且不需要任何安装。如果您更喜欢对实际数据库运行测试,可以使用 @AutoConfigureTestDatabase 注解,如下例所示:
Java
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 {
// ...
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
1.3.20. 自动配置 JDBC 测试
@JdbcTest 类似于 @DataJpaTest,但适用于仅需要 DataSource 且不使用 Spring Data JDBC 的测试。默认情况下,它配置一个内存中的嵌入式数据库和一个 JdbcTemplate。当使用 @JdbcTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@JdbcTest启用的自动配置列表可以在附录中找到。
默认情况下,JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,可以按如下方式禁用事务管理:
Java
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 {
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
如果您更喜欢对实际数据库运行测试,可以像对 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(请参阅 “自动配置 Data JPA 测试”。)
1.3.21. 自动配置 Data JDBC 测试
@DataJdbcTest 类似于 @JdbcTest,但适用于使用 Spring Data JDBC 存储库的测试。默认情况下,它配置一个内存中的嵌入式数据库、一个 JdbcTemplate 和 Spring Data JDBC 存储库。仅当使用 @DataJdbcTest 注解时扫描 AbstractJdbcConfiguration 子类,常规的 @Component 和 @ConfigurationProperties bean 不会被扫描。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@DataJdbcTest启用的自动配置列表可以在附录中找到。
默认情况下,Data JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,可以按如下方式禁用事务管理:
Java
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 {
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
如果您更喜欢对实际数据库运行测试,可以像对 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(请参阅 “自动配置 Data JPA 测试”。)
1.3.22. 自动配置 Data R2DBC 测试
@DataR2dbcTest 类似于 @DataJdbcTest,但适用于使用 Spring Data R2DBC 存储库的测试。默认情况下,它配置一个内存中的嵌入式数据库、一个 R2dbcEntityTemplate 和 Spring Data R2DBC 存储库。当使用 @DataR2dbcTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@DataR2dbcTest启用的自动配置列表可以在附录中找到。
默认情况下,Data R2DBC 测试不是事务性的。
如果您更喜欢对实际数据库运行测试,可以像对 @DataJpaTest 一样使用 @AutoConfigureTestDatabase 注解。(请参阅 “自动配置 Data JPA 测试”。)
1.3.23. 自动配置 jOOQ 测试
您可以像使用 @JdbcTest 一样使用 @JooqTest,但用于 jOOQ 相关测试。由于 jOOQ 严重依赖于与数据库架构对应的基于 Java 的架构,因此使用现有的 DataSource。如果您想将其替换为内存中的数据库,可以使用 @AutoConfigureTestDatabase 来覆盖这些设置。(有关使用 jOOQ 与 Spring Boot 的更多信息,请参阅 “使用 jOOQ”。)当使用 @JooqTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@JooqTest启用的自动配置列表可以在附录中找到。
@JooqTest 配置一个 DSLContext。以下示例显示了 @JooqTest 注解的使用:
Java
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;
// ...
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
JOOQ 测试默认是事务性的,并在每个测试结束时回滚。如果这不是你想要的,你可以为一个测试或整个测试类禁用事务管理,如JDBC 示例中所示。
1.3.24. 自动配置 Data MongoDB 测试
您可以使用 @DataMongoTest 来测试 MongoDB 应用程序。默认情况下,它配置一个内存中的嵌入式 MongoDB(如果可用),配置一个 MongoTemplate,扫描 @Document 类,并配置 Spring Data MongoDB 存储库。当使用 @DataMongoTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 MongoDB 与 Spring Boot 的更多信息,请参阅 “MongoDB”。)
Note:
@DataMongoTest启用的自动配置设置列表可以在附录中找到。
以下类显示了 @DataMongoTest 注解的使用:
Java
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;
// ...
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
内存中的嵌入式 MongoDB 通常适用于测试,因为它速度快且不需要任何开发人员安装。如果您更喜欢对实际 MongoDB 服务器运行测试,应排除嵌入式 MongoDB 自动配置,如下例所示:
Java
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class MyDataMongoDbTests {
// ...
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
1.3.25. 自动配置 Data Neo4j 测试
您可以使用 @DataNeo4jTest 来测试 Neo4j 应用程序。默认情况下,它扫描 @Node 类,并配置 Spring Data Neo4j 存储库。当使用 @DataNeo4jTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Neo4J 与 Spring Boot 的更多信息,请参阅 “Neo4j”。)
Note:
@DataNeo4jTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了在 Spring Boot 中使用 Neo4J 测试的典型设置:
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
@DataNeo4jTest
class MyDataNeo4jTests {
@Autowired
private SomeRepository repository;
// ...
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
默认情况下,Data Neo4j 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,可以按如下方式禁用事务管理:
Java
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 {
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Tip:事务性测试不支持反应式访问。如果您使用此样式,必须按上述描述配置
@DataNeo4jTest测试。
1.3.26. 自动配置 Data Redis 测试
您可以使用 @DataRedisTest 来测试 Redis 应用程序。默认情况下,它扫描 @RedisHash 类并配置 Spring Data Redis 存储库。当使用 @DataRedisTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 Redis 与 Spring Boot 的更多信息,请参阅 “Redis”。)
Note:
@DataRedisTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了 @DataRedisTest 注解的使用:
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
1.3.27. 自动配置 Data LDAP 测试
您可以使用 @DataLdapTest 来测试 LDAP 应用程序。默认情况下,它配置一个内存中的嵌入式 LDAP(如果可用),配置一个 LdapTemplate,扫描 @Entry 类,并配置 Spring Data LDAP 存储库。当使用 @DataLdapTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。(有关使用 LDAP 与 Spring Boot 的更多信息,请参阅 “LDAP”。)
Note:
@DataLdapTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了 @DataLdapTest 注解的使用:
Java
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;
// ...
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
内存中的嵌入式 LDAP 通常适用于测试,因为它速度快且不需要任何开发人员安装。如果您更喜欢对实际 LDAP 服务器运行测试,应排除嵌入式 LDAP 自动配置,如下例所示:
Java
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {
// ...
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
1.3.28. 自动配置 REST 客户端
您可以使用 @RestClientTest 注解来测试 REST 客户端。默认情况下,它自动配置 Jackson、GSON 和 Jsonb 支持,配置一个 RestTemplateBuilder,并添加对 MockRestServiceServer 的支持。当使用 @RestClientTest 注解时,不会扫描常规的 @Component 和 @ConfigurationProperties bean。可以使用 @EnableConfigurationProperties 来包含 @ConfigurationProperties bean。
Note:
@RestClientTest启用的自动配置设置列表可以在附录中找到。
应使用 @RestClientTest 的 value 或 components 属性指定要测试的特定 bean,如下例所示:
Java
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 MyRestClientTests {
@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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1.3.29. 自动配置 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 中。
1.3.29.1. 使用 Mock MVC 自动配置 Spring REST Docs 测试
@AutoConfigureRestDocs 自定义 MockMvc bean,以便在测试基于 servlet 的 Web 应用程序时使用 Spring REST Docs。您可以使用 @Autowired 注入它,并在测试中使用它,就像使用 Mock MVC 和 Spring REST Docs 时一样,如下例所示:
Java
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"));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
如果您需要比 @AutoConfigureRestDocs 属性提供的更多控制,可以使用 RestDocsMockMvcConfigurationCustomizer bean,如下例所示:
Java
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());
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
如果您希望利用 Spring REST Docs 对参数化输出目录的支持,可以创建一个 RestDocumentationResultHandler bean。自动配置调用 alwaysDo 与此结果处理程序,从而导致每个 MockMvc 调用自动生成默认片段。以下示例显示了定义 RestDocumentationResultHandler 的方法:
Java
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}");
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
1.3.29.2. 使用 WebTestClient 自动配置 Spring REST Docs 测试
@AutoConfigureRestDocs 也可以与 WebTestClient 一起使用,以测试响应式 Web 应用程序。您可以使用 @Autowired 注入它,并在测试中使用它,就像使用 @WebFluxTest 和 Spring REST Docs 时一样,如下例所示:
Java
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"));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
如果您需要比 @AutoConfigureRestDocs 属性提供的更多控制,可以使用 RestDocsWebTestClientConfigurationCustomizer bean,如下例所示:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
如果您希望利用 Spring REST Docs 对参数化输出目录的支持,可以使用 WebTestClientBuilderCustomizer 来配置每个实体交换结果的消费者。以下示例显示了定义 WebTestClientBuilderCustomizer 的方法:
Java
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}"));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
1.3.29.3. 使用 REST Assured 自动配置 Spring REST Docs 测试
@AutoConfigureRestDocs 使 RequestSpecification bean 可用于您的测试,该 bean 预配置为使用 Spring REST Docs。您可以使用 @Autowired 注入它,并在测试中使用它,就像使用 REST Assured 和 Spring REST Docs 时一样,如下例所示:
Java
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.restassured3.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));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
如果您需要比 @AutoConfigureRestDocs 属性提供的更多控制,可以使用 RestDocsRestAssuredConfigurationCustomizer bean,如下例所示:
Java
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.restdocs.restassured3.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());
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
1.3.30. 自动配置 Spring Web Services 测试
1.3.30.1. 自动配置 Spring Web Services 客户端测试
您可以使用 @WebServiceClientTest 来测试使用 Spring Web Services 项目调用 Web 服务的应用程序。默认情况下,它配置一个模拟的 WebServiceServer bean,并自动自定义您的 WebServiceTemplateBuilder。(有关使用 Web Services 与 Spring Boot 的更多信息,请参阅 “Web Services”。)
Note:
@WebServiceClientTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了 @WebServiceClientTest 注解的使用:
Java
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);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1.3.30.2. 自动配置 Spring Web Services 服务器测试
您可以使用 @WebServiceServerTest 来测试使用 Spring Web Services 项目实现 Web 服务的应用程序。默认情况下,它配置一个 MockWebServiceClient bean,可以用来调用您的 Web 服务端点。(有关使用 Web Services 与 Spring Boot 的更多信息,请参阅 “Web Services”。)
Note:
@WebServiceServerTest启用的自动配置设置列表可以在附录中找到。
以下示例显示了 @WebServiceServerTest 注解的使用:
Java
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>")));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1.3.31. 额外的自动配置和切片
每个切片提供一个或多个 @AutoConfigure… 注解,这些注解明确定义了应包含在切片中的自动配置。可以通过创建自定义 @AutoConfigure… 注解或将 @ImportAutoConfiguration 添加到测试中来逐个测试添加额外的自动配置,如下例所示:
Java
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 {
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Tip:请确保不使用常规的
@Import注解来导入自动配置,因为它们由 Spring Boot 以特定方式处理。
或者,可以为任何使用切片注解的情况注册额外的自动配置,方法是将它们存储在 META-INF/spring 中的文件中,如下例所示:
Properties
com.example.IntegrationAutoConfiguration在此示例中,com.example.IntegrationAutoConfiguration 在每个使用 @JdbcTest 注解的测试中启用。
Note:您可以在此文件中使用
#进行注释。
Note:只要切片或
@AutoConfigure…注解用@ImportAutoConfiguration进行元注解,就可以这样自定义它。
1.3.32. 用户配置和切片
如果您合理地结构化代码,您的 @SpringBootApplication 类将默认用作测试的配置。
因此,重要的是不要在应用程序的主类中添加特定于某个区域的配置设置。
假设您使用 Spring Batch,并依赖其自动配置。您可以将 @SpringBootApplication 定义如下:
Java
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {
// ...
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
由于此类是测试的源配置,任何切片测试实际上都会尝试启动 Spring Batch,这显然不是您想要做的。推荐的方法是将该特定于区域的配置移动到与您的应用程序相同级别的单独 @Configuration 类中,如下例所示:
Java
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class MyBatchConfiguration {
// ...
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Tip:根据应用程序的复杂性,您可能会有一个用于自定义的单独
@Configuration类,也可能会有一个每个领域的类。后一种方法让您在必要时可以使用@Import注解在测试中启用它。有关何时可能需要为切片测试启用特定@Configuration类的更多详细信息,请参阅此操作指南部分。
测试切片排除 @Configuration 类进行扫描。例如,对于 @WebMvcTest,以下配置将不会在测试切片加载的应用程序上下文中包含给定的 WebMvcConfigurer bean:
Java
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() {
// ...
};
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
但是,以下配置将导致测试切片加载自定义的 WebMvcConfigurer:
Java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
// ...
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
另一个混淆来源是类路径扫描。假设在合理结构化代码的同时,您需要扫描额外的包。您的应用程序可能如下所示:
Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
这样做实际上覆盖了默认的组件扫描指令,副作用是无论您选择哪个切片,都会扫描这两个包。例如,@DataJpaTest 似乎突然扫描应用程序的组件和用户配置。同样,将自定义指令移动到单独的类是解决此问题的好方法。
Note:如果这对您来说不是一个选项,您可以在测试的某个层次结构中创建一个
@SpringBootConfiguration,以便使用它。或者,您可以指定测试的源,这将禁用查找默认源的行为。
1.3.33. 使用 Spock 测试 Spring Boot 应用程序
Spock 2.x 可以用来测试 Spring Boot 应用程序。要做到这一点,请将依赖项添加到应用程序构建中的 Spock 的 spock-spring 模块。spock-spring 将 Spring 的测试框架集成到 Spock 中。有关更多详细信息,请参阅 Spock 的 Spring 模块文档。
1.4. 测试实用程序
一些在测试应用程序时通常有用的测试实用程序类被打包为 spring-boot 的一部分。
1.4.1. ConfigDataApplicationContextInitializer
ConfigDataApplicationContextInitializer 是一个 ApplicationContextInitializer,您可以将其应用于测试以加载 Spring Boot application.properties 文件。当您不需要 @SpringBootTest 提供的全套功能时,可以使用它,如下例所示:
Java
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Tip:仅使用
ConfigDataApplicationContextInitializer不提供对@Value("${…}")注入的支持。它的唯一作用是确保application.properties文件被加载到 Spring 的Environment中。要支持@Value,您需要额外配置一个PropertySourcesPlaceholderConfigurer,或者使用@SpringBootTest,它会为您自动配置一个。
1.4.2. TestPropertyValues
TestPropertyValues 允许您快速向 ConfigurableEnvironment 或 ConfigurableApplicationContext 添加属性。您可以使用 key=value 字符串调用它,如下所示:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.4.3. OutputCapture
OutputCapture 是一个 JUnit Extension,您可以使用它来捕获 System.out 和 System.err 输出。要使用它,请添加 @ExtendWith(OutputCaptureExtension.class),并将 CapturedOutput 作为参数注入到您的测试类构造函数或测试方法中,如下所示:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.4.4. TestRestTemplate
TestRestTemplate 是 Spring 的 RestTemplate 的便捷替代品,在集成测试中非常有用。您可以获得一个普通的模板,或者一个发送基本 HTTP 身份验证(带有用户名和密码)的模板。在任何一种情况下,模板都是容错的。这意味着它在 4xx 和 5xx 错误时不会抛出异常,而是通过返回的 ResponseEntity 和其状态码检测这些错误。
Note:Spring Framework 5.0 提供了一个新的
WebTestClient,适用于 WebFlux 集成测试和 WebFlux 和 MVC 端到端测试。它提供了一个流式的 API 用于断言,而不像TestRestTemplate。
建议但不强制使用 Apache HTTP Client(版本 4.3.2 或更高版本)。如果您的类路径上有它,TestRestTemplate 会通过适当配置客户端来响应。如果您使用 Apache 的 HTTP 客户端,将启用一些额外的测试友好功能:
- 不跟踪重定向(因此您可以断言响应位置)。
- 忽略 Cookie(因此模板是无状态的)。
TestRestTemplate 可以直接在集成测试中实例化,如下例所示:
Java
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");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
或者,如果您使用 @SpringBootTest 注解与 WebEnvironment.RANDOM_PORT 或 WebEnvironment.DEFINED_PORT,您可以注入一个完全配置的 TestRestTemplate 并开始使用它。如果需要,可以通过 RestTemplateBuilder bean 应用额外的自定义。任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如下例所示:
Java
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));
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2. 创建自己的自动配置
如果你在一个开发共享库的公司工作,或者你在一个开源或商业库上工作,你可能想要开发自己的自动配置。自动配置类可以打包在外部 jar 文件中,并且仍然可以被 Spring Boot 检测到。
自动配置可以与一个 “Starter” 相关联,该 Starter 提供自动配置代码以及你通常会与之一起使用的库。我们首先介绍你需要了解的内容来构建自己的自动配置,然后我们将介绍创建自定义 Starter 所需的典型步骤。
2.1. 理解自动配置的 Bean
实现自动配置的类使用 @AutoConfiguration 注解。该注解本身被 @Configuration 元注解,使自动配置成为标准的 @Configuration 类。额外的 @Conditional 注解用于约束自动配置何时应用。通常,自动配置类使用 @ConditionalOnClass 和 @ConditionalOnMissingBean 注解。这确保了自动配置仅在找到相关类并且你没有声明自己的 @Configuration 时应用。
你可以浏览 spring-boot-autoconfigure 的源代码,查看 Spring 提供的 @AutoConfiguration 类(参见 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件)。
2.2. 定位自动配置候选项
Spring Boot 会检查你发布的 jar 文件中是否存在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。该文件应列出你的配置类,每行一个类名,如下例所示:
Properties
com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfigurationNote:你可以使用
#字符在 imports 文件中添加注释。
Tip:自动配置必须仅通过在 imports 文件中命名来加载。确保它们定义在特定的包空间中,并且它们永远不会成为组件扫描的目标。此外,自动配置类不应启用组件扫描来查找额外的组件。应使用特定的
@Import代替。
如果你的配置需要按特定顺序应用,你可以使用 before、beforeName、after 和 afterName 属性在 @AutoConfiguration 注解或专用的 @AutoConfigureBefore 和 @AutoConfigureAfter 注解上。例如,如果你提供特定于 Web 的配置,你的类可能需要在 WebMvcAutoConfiguration 之后应用。
如果你想对不应直接相互了解的某些自动配置进行排序,你还可以使用 @AutoConfigureOrder。该注解与常规的 @Order 注解具有相同的语义,但为自动配置类提供了专用的顺序。
与标准的 @Configuration 类一样,自动配置类应用的顺序仅影响它们的 bean 定义的顺序。这些 bean 随后创建的顺序不受影响,并由每个 bean 的依赖关系和任何 @DependsOn 关系决定。
2.3. 条件注解
你几乎总是希望在自动配置类上包含一个或多个 @Conditional 注解。@ConditionalOnMissingBean 注解是一个常见的例子,它用于允许开发人员在不满意你的默认设置时覆盖自动配置。
Spring Boot 包含许多 @Conditional 注解,你可以通过注解 @Configuration 类或单个 @Bean 方法在自己的代码中重用这些注解。这些注解包括:
2.3.1. 类条件
@ConditionalOnClass 和 @ConditionalOnMissingClass 注解允许 @Configuration 类根据特定类的存在或缺失包含在内。由于注解元数据是使用 ASM 解析的,你可以使用 value 属性引用实际类,即使该类可能实际上不出现在运行应用程序的类路径上。你也可以使用 name 属性,如果你更喜欢使用 String 值指定类名。
此机制不以相同的方式应用于 @Bean 方法,通常返回类型是条件的目标:在方法上的条件应用之前,JVM 将加载该类并可能处理方法引用,如果该类不存在则会失败。
为了处理这种情况,可以使用单独的 @Configuration 类来隔离条件,如下例所示:
Java
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@AutoConfiguration
// 一些条件 ...
public class MyAutoConfiguration {
// 自动配置的 bean ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Note:如果你将
@ConditionalOnClass或@ConditionalOnMissingClass作为元注解的一部分来组合你自己的注解,你必须使用name,因为在这种情况下不处理引用类。
2.3.2. Bean 条件
@ConditionalOnBean 和 @ConditionalOnMissingBean 注解允许根据特定 bean 的存在或缺失包含一个 bean。你可以使用 value 属性按类型指定 bean,或使用 name 按名称指定 bean。search 属性允许你限制在搜索 bean 时应考虑的 ApplicationContext 层次结构。
当放置在 @Bean 方法上时,目标类型默认为方法的返回类型,如下例所示:
Java
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
在上例中,如果 ApplicationContext 中不包含 SomeService 类型的 bean,则会创建 someService bean。
Note:你需要非常小心 bean 定义添加的顺序,因为这些条件是根据迄今为止处理的内容进行评估的。因此,我们建议仅在自动配置类上使用
@ConditionalOnBean和@ConditionalOnMissingBean注解(因为这些类保证在添加任何用户定义的 bean 定义之后加载)。
Tip:
@ConditionalOnBean和@ConditionalOnMissingBean不会阻止创建@Configuration类。在类级别使用这些条件与标记每个包含的@Bean方法的注解的唯一区别是,前者在条件不匹配时防止注册@Configuration类为 bean。
Note:在声明
@Bean方法时,在方法的返回类型中提供尽可能多的类型信息。例如,如果你的 bean 的具体类实现了一个接口,bean 方法的返回类型应该是具体类而不是接口。在@Bean方法中提供尽可能多的类型信息,特别是在使用 bean 条件时,因为它们的评估只能依赖于方法签名中可用的类型信息。
2.3.3. 属性条件
@ConditionalOnProperty 注解允许根据 Spring Environment 属性包含配置。使用 prefix 和 name 属性指定应检查的属性。默认情况下,任何存在且不等于 false 的属性都匹配。你还可以使用 havingValue 和 matchIfMissing 属性创建更高级的检查。
2.3.4. 资源条件
@ConditionalOnResource 注解允许仅在存在特定资源时包含配置。可以使用常见的 Spring 约定指定资源,如下例所示:file:/home/user/test.dat。
2.3.5. Web 应用程序条件
@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication 注解允许根据应用程序是否为 Web 应用程序包含配置。基于 servlet 的 Web 应用程序是任何使用 Spring WebApplicationContext、定义 session 作用域或具有 ConfigurableWebEnvironment 的应用程序。基于响应式的 Web 应用程序是任何使用 ReactiveWebApplicationContext 或具有 ConfigurableReactiveWebEnvironment 的应用程序。
@ConditionalOnWarDeployment 和 @ConditionalOnNotWarDeployment 注解允许根据应用程序是否为传统的 WAR 应用程序(部署到 servlet 容器)包含配置。此条件不会匹配运行嵌入式 Web 服务器的应用程序。
2.3.6. SpEL 表达式条件
@ConditionalOnExpression 注解允许根据 SpEL 表达式的结果包含配置。
Tip:在表达式中引用 bean 会导致该 bean 在上下文刷新处理的早期初始化。结果,该 bean 不符合后处理条件(如配置属性绑定),并且其状态可能不完整。
2.4. 测试你的自动配置
自动配置可能受到许多因素的影响:用户配置(@Bean 定义和 Environment 自定义)、条件评估(特定库的存在)等。具体来说,每个测试应创建一个明确定义的 ApplicationContext,代表这些自定义的组合。ApplicationContextRunner 提供了实现这一点的好方法。
ApplicationContextRunner 通常定义为测试类的字段,以收集基本的、共同的配置。以下示例确保始终调用 MyServiceAutoConfiguration:
Java
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));1
2
2
Note:如果需要定义多个自动配置,无需对它们的声明进行排序,因为它们的调用顺序与运行应用程序时完全相同。
每个测试可以使用 runner 表示特定用例。例如,下面的示例调用用户配置(UserConfiguration)并检查自动配置是否正确退出。调用 run 提供一个回调上下文,可以与 AssertJ 一起使用。
Java
@Test
void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
MyService myCustomService() {
return new MyService("mine");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
还可以轻松自定义 Environment,如下例所示:
Java
@Test
void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
});
}1
2
3
4
5
6
7
2
3
4
5
6
7
runner 还可以用于显示 ConditionEvaluationReport。报告可以在 INFO 或 DEBUG 级别打印。以下示例显示如何在自动配置测试中使用 ConditionEvaluationReportLoggingListener 打印报告。
Java
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class MyConditionEvaluationReportingTests {
@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.4.1. 模拟 Web 上下文
如果你需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请分别使用 WebApplicationContextRunner 或 ReactiveWebApplicationContextRunner。
2.4.2. 覆盖类路径
还可以测试在运行时某个类和/或包不存在时会发生什么。Spring Boot 附带了一个 FilteredClassLoader,可以轻松地被 runner 使用。在以下示例中,我们断言如果 MyService 不存在,自动配置会被正确禁用:
Java
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
.run((context) -> assertThat(context).doesNotHaveBean("myService"));
}1
2
3
4
5
2
3
4
5
2.5. 创建自己的 Starter
典型的 Spring Boot Starter 包含代码,用于自动配置和自定义某个技术的基础设施,我们称之为 “acme”。为了使其易于扩展,可以将一些配置键暴露给环境的专用命名空间。最后,提供一个单独的 “Starter” 依赖项,以帮助用户尽可能轻松地开始使用。
具体来说,自定义 Starter 可以包含以下内容:
包含 “acme” 自动配置代码的
autoconfigure模块。提供对
autoconfigure模块以及 “acme” 和任何其他通常有用的依赖项的依赖的starter模块。简而言之,添加 Starter 应提供使用该库所需的一切。
这种分离为两个模块并不是必须的。如果 “acme” 有几种风味、选项或可选功能,那么将自动配置分开是更好的选择。这样,你可以清楚地表达某些功能是可选的。同时,你有能力制作一个 Starter,提供对这些可选依赖项的看法。同时,其他人可以仅依赖 autoconfigure 模块,并制作具有不同看法的 Starter。
如果自动配置相对简单且没有可选功能,将两个模块合并到 Starter 中是一个选项。
2.5.1. 命名
你应确保为 Starter 提供适当的命名空间。不要以 spring-boot 开头命名你的模块名称,即使你使用不同的 Maven groupId。我们可能会在未来为你自动配置的东西提供官方支持。
作为一个经验法则,你应该根据 Starter 命名组合模块。例如,假设你正在为 “acme” 创建 Starter,并且你将自动配置模块命名为 acme-spring-boot,Starter 命名为 acme-spring-boot-starter。如果你只有一个组合两者的模块,请将其命名为 acme-spring-boot-starter。
2.5.2. 配置键
如果你的 Starter 提供配置键,请为它们使用唯一的命名空间。特别是,不要将你的键包含在 Spring Boot 使用的命名空间中(如 server、management、spring 等)。如果你使用相同的命名空间,我们可能会在未来以破坏你的模块的方式修改这些命名空间。作为一个经验法则,请使用你拥有的命名空间(例如 acme)作为所有键的前缀。
确保通过为每个属性添加字段 Javadoc 来记录配置键,如下例所示:
Java
import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("acme")
public class AcmeProperties {
/**
* 是否检查 acme 资源的位置。
*/
private boolean checkLocation = true;
/**
* 连接到 acme 服务器的超时时间。
*/
private Duration loginTimeout = Duration.ofSeconds(3);
public boolean isCheckLocation() {
return this.checkLocation;
}
public void setCheckLocation(boolean checkLocation) {
this.checkLocation = checkLocation;
}
public Duration getLoginTimeout() {
return this.loginTimeout;
}
public void setLoginTimeout(Duration loginTimeout) {
this.loginTimeout = loginTimeout;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Tip:你应该仅使用纯文本与
@ConfigurationProperties字段 Javadoc,因为在添加到 JSON 之前不会处理它们。
以下是我们内部遵循的一些规则,以确保描述一致:
不要以 “The” 或 “A” 开头描述。
对于
boolean类型,以 “Whether” 或 “Enable” 开头描述。对于基于集合的类型,以 “Comma-separated list” 开头描述。
使用
java.time.Duration而不是long,并描述默认单位(如果与毫秒不同),例如 “如果未指定持续时间后缀,将使用秒”。除非必须在运行时确定,否则不要在描述中提供默认值。
确保触发元数据生成,以便你的键也可以使用 IDE 辅助。你可能希望查看生成的元数据(META-INF/spring-configuration-metadata.json),以确保你的键得到了正确的记录。在兼容的 IDE 中使用你自己的 Starter 也是验证元数据质量的好方法。
2.5.3. “autoconfigure” 模块
autoconfigure 模块包含开始使用该库所需的一切。它还可以包含配置键定义(如 @ConfigurationProperties)和任何回调接口,可用于进一步自定义组件的初始化方式。
Note:你应将对库的依赖项标记为可选,以便你可以更轻松地将
autoconfigure模块包含在你的项目中。如果你这样做,库不提供,默认情况下 Spring Boot 会退出。
Spring Boot 使用注解处理器将自动配置的条件收集到元数据文件(META-INF/spring-autoconfigure-metadata.properties)中。如果存在该文件,则使用它来过滤不匹配的自动配置,从而提高启动时间。
在使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖项:
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>如果你在应用程序中直接定义了自动配置,请确保配置 spring-boot-maven-plugin,以防止 repackage 目标将依赖项添加到 fat jar 中:
XML
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>使用 Gradle 时,应在 annotationProcessor 配置中声明依赖项,如下例所示:
Gradle
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}2.5.4. Starter 模块
Starter 实际上是一个空的 jar。它的唯一目的是提供使用该库所需的依赖项。你可以将其视为对开始使用所需内容的一种有见解的看法。
不要对添加 Starter 的项目做出假设。如果你自动配置的库通常需要其他 Starter,请提及它们。提供一组适当的默认依赖项可能很难,因为可选依赖项的数量很高,因为你应避免包含对库的典型使用不必要的依赖项。换句话说,你不应包含可选依赖项。
Tip:无论如何,你的 Starter 必须直接或间接引用核心 Spring Boot Starter(
spring-boot-starter)(如果你的 Starter 依赖于另一个 Starter,则无需添加它)。如果一个项目仅使用你的自定义 Starter 创建,Spring Boot 的核心功能将通过核心 Starter 的存在得到尊重。
3. Kotlin 支持
Kotlin 是一种面向 JVM(以及其他平台)的静态类型语言,允许编写简洁优雅的代码,同时提供与现有 Java 编写的库的互操作性。
Spring Boot 通过利用其他 Spring 项目(如 Spring Framework、Spring Data 和 Reactor)的支持来提供 Kotlin 支持。有关更多信息,请参阅 Spring Framework Kotlin 支持文档。
开始使用 Spring Boot 和 Kotlin 的最简单方法是按照这个全面的教程进行操作。你可以使用 start.spring.io 创建新的 Kotlin 项目。如果你需要支持,可以加入 Kotlin Slack 的 spring 频道,或者在 Stack Overflow 上使用 spring 和 kotlin 标签提问。
3.1. 要求
Spring Boot 至少需要 Kotlin 1.3.x,并通过依赖管理管理合适的 Kotlin 版本。要使用 Kotlin,org.jetbrains.kotlin:kotlin-stdlib 和 org.jetbrains.kotlin:kotlin-reflect 必须存在于类路径中。还可以使用 kotlin-stdlib 变体 kotlin-stdlib-jdk7 和 kotlin-stdlib-jdk8。
由于 Kotlin 类默认是 final 的,你可能希望配置 kotlin-spring 插件,以便自动打开 Spring 注解的类,以便它们可以被代理。
Jackson 的 Kotlin 模块是序列化/反序列化 Kotlin 中的 JSON 数据所必需的。当在类路径中找到时,它会自动注册。如果存在 Jackson 和 Kotlin,但没有 Jackson Kotlin 模块,则会记录警告消息。
Note:如果在 start.spring.io 上引导一个 Kotlin 项目,这些依赖项和插件会默认提供。
3.2. Null 安全
Kotlin 的一个关键特性是 Null 安全。它在编译时处理 null 值,而不是将问题推迟到运行时并遇到 NullPointerException。这有助于消除常见的错误来源,而不需要支付像 Optional 这样的包装器的代价。Kotlin 还允许使用可空值的函数构造,如这篇关于 Kotlin Null 安全的全面指南中所述。
尽管 Java 不允许在其类型系统中表达 Null 安全,但 Spring Framework、Spring Data 和 Reactor 现在通过工具友好的注解为其 API 提供 Null 安全。默认情况下,从 Java API 使用的类型在 Kotlin 中被识别为平台类型,对这些类型的空检查是放宽的。Kotlin 对 JSR 305 注解的支持结合空性注解为相关的 Spring API 提供了 Kotlin 中的 Null 安全。
可以通过添加 -Xjsr305 编译器标志来配置 JSR 305 检查,其选项为 -Xjsr305={strict|warn|ignore}。默认行为与 -Xjsr305=warn 相同。strict 值要求在 Kotlin 类型中考虑 Null 安全,但应该知道 Spring API 的空性声明可能会在次要版本之间发生变化,并且未来可能会添加更多检查。
Warning:泛型类型参数、可变参数和数组元素的空性尚不支持。请参阅 SPR-15942 获取最新信息。还请注意,Spring Boot 的自身 API 尚未注解。
3.3. Kotlin API
3.3.1. runApplication
Spring Boot 提供了一种使用 runApplication<MyApplication>(*args) 运行应用程序的惯用方式,如下例所示:
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
这是 SpringApplication.run(MyApplication::class.java, *args) 的替代品。它还允许自定义应用程序,如下例所示:
Kotlin
runApplication<MyApplication>(*args) {
setBannerMode(OFF)
}1
2
3
2
3
3.3.2. 扩展
Kotlin 扩展提供了扩展现有类的功能。Spring Boot Kotlin API 利用这些扩展,为现有 API 添加新的 Kotlin 特定便利功能。
提供了类似于 Spring Framework 为 Spring Framework 中的 RestOperations 提供的 TestRestTemplate 扩展。除其他功能外,这些扩展使得可以利用 Kotlin 具体化类型参数。
3.4. 依赖管理
为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。
在 Maven 中,可以通过设置 kotlin.version 属性来自定义 Kotlin 版本,并为 kotlin-maven-plugin 提供插件管理。在 Gradle 中,Spring Boot 插件会自动将 kotlin.version 与 Kotlin 插件的版本对齐。
Spring Boot 还通过导入 Kotlin Coroutines BOM 来管理 Coroutines 依赖项的版本。可以通过设置 kotlin-coroutines.version 属性来自定义版本。
Note:如果在 start.spring.io 上引导一个具有至少一个响应式依赖项的 Kotlin 项目,则默认提供
org.jetbrains.kotlinx:kotlinx-coroutines-reactor依赖项。
3.5. @ConfigurationProperties
当 @ConfigurationProperties 与 @ConstructorBinding 结合使用时,支持具有不可变 val 属性的类,如下例所示:
Kotlin
@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {
data class MyService(
val apiToken: String,
val uri: URI
)
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Note:要使用注解处理器生成你自己的元数据,请使用
spring-boot-configuration-processor依赖项配置kapt。请注意,由于 kapt 提供的模型的限制,某些功能(如检测默认值或弃用项)尚未实现。
3.6. 测试
虽然可以使用 JUnit 4 测试 Kotlin 代码,但 JUnit 5 默认提供并推荐使用。JUnit 5 使测试类可以实例化一次并重用所有类的测试。这使得可以在非静态方法上使用 @BeforeAll 和 @AfterAll 注解,这对 Kotlin 是一个很好的选择。
要模拟 Kotlin 类,推荐使用 MockK。如果你需要 MockK 等效于 Mockito 特定的 @MockBean 和 @SpyBean 注解,可以使用 SpringMockK,它提供了类似的 @MockkBean 和 @SpykBean 注解。
3.7. 资源
3.7.1. 进一步阅读
- Kotlin 语言参考
- Kotlin Slack(带有专用的 spring 频道)
- Stackoverflow 带有
spring和kotlin标签 - 在浏览器中尝试 Kotlin
- Kotlin 博客
- Awesome Kotlin
- 教程:使用 Spring Boot 和 Kotlin 构建 Web 应用程序
- 使用 Kotlin 开发 Spring Boot 应用程序
- 使用 Kotlin、Spring Boot 和 PostgreSQL 的地理空间消息传递器
- 介绍 Spring Framework 5.0 中的 Kotlin 支持
- Spring Framework 5 Kotlin API,功能化方式
3.7.2. 示例
- spring-boot-kotlin-demo:常规 Spring Boot + Spring Data JPA 项目
- mixit:Spring Boot 2 + WebFlux + Reactive Spring Data MongoDB
- spring-kotlin-fullstack:WebFlux Kotlin 全栈示例,使用 Kotlin2js 而不是 JavaScript 或 TypeScript 进行前端开发
- spring-petclinic-kotlin:Spring PetClinic 示例应用程序的 Kotlin 版本
- spring-kotlin-deepdive:从 Boot 1.0 + Java 到 Boot 2.0 + Kotlin 的逐步迁移
- spring-boot-coroutines-demo:Coroutines 示例项目
4. 接下来阅读什么
如果你想了解更多关于本节讨论的任何类的信息,请参阅 Spring Boot API 文档,或者你可以直接浏览源代码。如果你有具体问题,请参阅操作指南部分。
如果你对 Spring Boot 的核心功能感到满意,可以继续阅读生产就绪功能。