Spring Boot Testing中文文档

2023-12-18 22:49:24

本文为官方文档直译版本。原文链接

引言

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-starter-test “Starter”(在test``scope内)包含以下提供的库:

  • JUnit 5:Java 应用程序单元测试的事实标准。
  • Spring Test 和 Spring Boot Test: 为 Spring Boot 应用程序提供实用程序和集成测试支持。
  • AssertJ:流畅的断言库。
  • Hamcrest: 一个匹配器对象库(也称为约束或谓词)。
  • Mockito:一个 Java 模拟框架: 一个 Java 模拟框架。
  • JSONassert: 一个 JSON 断言库。
  • JsonPath:JSON 的 XPath: JSON 的 XPath。
  • Awaitility:用于测试异步系统的库: 用于测试异步系统的库。

在编写测试时,我们通常会发现这些常用库非常有用。如果这些库不能满足您的需求,您可以自行添加其他测试依赖库。

测试 Spring 应用程序

依赖注入的一个主要优势是,它可以让你的代码更容易进行单元测试。您可以使用 new 操作符实例化对象,而无需涉及 Spring。您还可以使用模拟对象来代替真实的依赖关系。
通常情况下,你需要超越单元测试,开始集成测试(使用 Spring ApplicationContext)。无需部署应用程序或连接其他基础架构就能执行集成测试是非常有用的。
Spring 框架包含一个专用测试模块,用于此类集成测试。您可以直接向 org.springframework:spring-test 声明依赖关系,或使用 spring-boot-starter-test "Starter "将其临时引入。
如果你以前没有使用过 spring-test 模块,应该先阅读 Spring Framework 参考文档的相关章节。

测试 Spring Boot 应用程序

Spring Boot 应用程序是一个 Spring ApplicationContext,因此除了通常使用普通 Spring 上下文进行测试外,不需要做任何特别的测试。

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

Spring Boot 提供了 @SpringBootTest 注解,当你需要 Spring Boot 功能时,可以用它来替代标准的 spring-test @ContextConfiguration 注解。该注解的工作原理是通过 SpringApplication 创建测试中使用的 ApplicationContext。除了 @SpringBootTest 之外,还提供了许多其他注解,用于测试应用程序的更多特定片段。

如果使用的是 JUnit 4,请不要忘记在测试中添加 @RunWith(SpringRunner.class),否则注解将被忽略。如果使用的是 JUnit 5,则无需添加 @ExtendWith(SpringExtension.class),因为 @SpringBootTest 和其他 @...Test 注释都已使用了该注解。

默认情况下,@SpringBootTest 不会启动服务器。你可以使用 @SpringBootTestwebEnvironment 属性来进一步完善测试的运行方式:

  • MOCK(默认): 加载网络 ApplicationContext 并提供模拟网络环境。使用此注解时,不会启动嵌入式服务器。如果classpath 上没有网络环境,该模式会透明地退回到创建普通的非网络 ApplicationContext。它可与 @AutoConfigureMockMvc@AutoConfigureWebTestClient 结合使用,用于对网络应用程序进行基于模拟的测试。
  • RANDOM_PORT:加载 WebServerApplicationContext 并提供真实的网络环境。嵌入式服务器会启动并监听一个随机端口。
  • DEFINED_PORT:加载 WebServerApplicationContext 并提供真实的网络环境。嵌入式服务器将被启动,并监听一个已定义的端口(application.properties)或默认的 8080 端口。
  • NONE: 使用 SpringApplication 加载 ApplicationContext,但不提供任何网络环境(模拟或其他)。

如果您的测试是 @Transactional,那么默认情况下,它会在每个测试方法结束时回滚事务。不过,由于使用 RANDOM_PORTDEFINED_PORT 会隐式地提供一个真正的 servlet 环境,HTTP 客户端和服务器会在不同的线程中运行,因此也会在不同的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。

@SpringBootTest 设置 webEnvironment = WebEnvironment.RANDOM_PORT 还会在一个单独的随机端口上启动管理服务器,如果你的应用程序为管理服务器使用了不同的端口的话。

检测 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 的测试框架会在测试之间缓存应用程序上下文。因此,只要你的测试共享相同的配置(无论配置是如何被发现的),加载上下文这一可能耗时的过程就只会发生一次。

使用测试配置主方法

通常情况下,@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);
    }

}

在上面的示例中,main 方法除了委托 SpringApplication.run 以外没有做任何其他事情。不过,也可以使用更复杂的 main 方法,在调用 SpringApplication.run 之前应用自定义功能。
例如,下面是一个更改横幅模式和设置附加配置文件的应用程序:

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

}

由于main方法中的自定义会影响生成的ApplicationContext,所以你也可能想使用main方法来创建测试中使用的ApplicationContext。默认情况下,@SpringBootTest 不会调用主方法,而是直接使用类本身来创建 ApplicationContext
如果想改变这种行为,可以将 @SpringBootTestuseMainMethod 属性改为 UseMainMethod.ALWAYSUseMainMethod.WHEN_AVAILABLE。当设置为 ALWAYS 时,如果找不到主方法,测试就会失败。设置为 WHEN_AVAILABLE 时,如果主方法可用,则将使用该方法,否则将使用标准加载机制。
例如,下面的测试将调用 MyApplication 的主方法来创建 ApplicationContext。如果主方法设置了附加配置文件,那么这些配置文件将在 ApplicationContext 启动时处于活动状态。

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。详情请参见 Javadoc。

导入的 @TestConfiguration 比内类的 @TestConfiguration 处理得更早,而且导入的 @TestConfiguration 将在通过组件扫描发现的任何配置之前被处理。一般来说,这种排序上的差异不会产生明显的影响,但如果您依赖 bean 重载,则需要注意这一点。

使用应用程序参数

如果应用程序需要参数,可以让 @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");
    }

}

使用模拟环境进行测试

默认情况下,@SpringBootTest 不会启动服务器,而是为测试 Web 端点设置一个模拟环境。
使用 Spring MVC,我们可以使用 [MockMvc](https://docs.spring.io/spring-framework/reference/testing/spring-mvc-test-framework.html)WebTestClient 查询网络端点,如下例所示:

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

}


如果只想关注网络层而不想启动完整的 ApplicationContext,可以考虑使用 @WebMvcTest 代替。

通过 Spring WebFlux 端点,您可以使用 [WebTestClient](https://docs.spring.io/spring-framework/reference/testing/webtestclient.html),如下例所示:

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 都会被用于创建 WebTestClientWebTestClient.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");
        // ...
    }

}

使用指标

在使用 @SpringBootTest 时,无论你的类路径如何,除了内存备份外,度量表注册表都不会自动配置。
如果需要将指标导出到不同的后端作为集成测试的一部分,请使用 @AutoConfigureObservability 进行注释。

使用跟踪

使用 @SpringBootTest 时,无论你的类路径如何,报告数据的跟踪组件都不会自动配置。
如果需要将这些组件作为集成测试的一部分,请使用 @AutoConfigureObservability 对测试进行注释。
如果你创建了自己的报告组件(例如自定义 SpanExporterSpanHandler),并且不想让它们在测试中激活,可以使用 @ConditionalOnEnabledTracing 注解来禁用它们。

模拟和 Spying Beans

运行测试时,有时需要在应用程序上下文中模拟某些组件。例如,您可能会在某个远程服务上设置一个门面,而该服务在开发过程中是不可用的。当您想模拟在真实环境中很难触发的故障时,模拟也很有用。
Spring Boot 包含一个 @MockBean 注解,可用于为 ApplicationContext 中的 Bean 定义 Mockito mock。您可以使用注解添加新的 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。详情请参见 Javadoc。

Spring 的测试框架会在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,但 @MockBean@SpyBean 的使用会影响缓存键,这很可能会增加上下文的数量。

如果您使用 @SpyBean 来监视带有 @Cacheable 方法的 Bean,而这些方法会通过名称引用参数,那么您的应用程序在编译时必须使用 -parameters。这样可以确保一旦 Bean 被监视,缓存基础架构就可以使用参数名称。

当您使用 @SpyBean 监视由 Spring 代理的 Bean 时,您可能需要在某些情况下移除 Spring 的代理,例如在使用 givenwhen 设置期望值时。请使用 AopTestUtils.getTargetObject(yourProxiedSpy) 来移除代理。

自动配置测试

Spring Boot 的自动配置系统在应用程序中运行良好,但有时对测试来说可能有点太多。通常只加载测试应用程序 "片段 "所需的配置部分会有帮助。例如,您可能想测试 Spring MVC 控制器是否正确映射了 URL,而您不想在这些测试中涉及数据库调用,或者您可能想测试 JPA 实体,而您对这些测试运行时的 Web 层不感兴趣。
spring-boot-test-autoconfigure 模块包含大量注解,可用于自动配置此类 “切片”。每个注解的工作方式类似,都提供了一个用于加载 ApplicationContext@...Test 注解,以及一个或多个用于自定义自动配置设置的 @AutoConfigure...annotation 注解。

每个片段都会将组件扫描限制在适当的组件上,并加载一组非常有限的自动配置类。如果需要排除其中一个,大多数 @...Test 注解都提供了一个 excludeAutoConfiguration 属性。或者,您也可以使用 @ImportAutoConfiguration#exclude

不支持在一个测试中使用多个 @...Test 注释来包含多个 “片段”。如果需要多个 “片段”,请选择其中一个 @...Test 注释,然后手工添加其他 "片段 "的 @AutoConfigure...annotations

也可以将 @AutoConfigure... 注解与标准的 @SpringBootTest 注解结合使用。如果你对 "切分 "应用程序不感兴趣,但又想要一些自动配置的测试 Bean,那么就可以使用这种组合。

自动配置 JSON 测试

要测试对象 JSON 序列化和反序列化是否按预期运行,可以使用 @JsonTest 注解。@JsonTest 会自动配置可用的受支持 JSON 映射器,该映射器可以是以下库之一:

  • Jackson ObjectMapper、任何 @JsonComponent Bean 和任何 Jackson 模块
  • Gson
  • Jsonb

@JsonTest 启用的自动配置列表见附录。

如果需要配置自动配置的元素,可以使用 @AutoConfigureJsonTesters 注解。
Spring Boot 包含基于 AssertJ 的帮助程序,可与 JSONAssert 和 JsonPath 库配合使用,检查 JSON 是否按预期显示。JacksonTesterGsonTesterJsonbTesterBasicJsonTester 类可分别用于 Jackson、Gson、Jsonb 和 Strings。使用 @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.15 的浮点数值,偏移量为 0.01

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

@WebMvcTest 启用的自动配置设置列表见附录。

如果需要注册额外的组件(如 Jackson 模块),可以通过在测试中使用 @Import 来导入额外的配置类。

通常,@WebMvcTest 仅限于单个控制器,并与 @MockBean 结合使用,为所需的协作者提供模拟实现。
@WebMvcTest 还能自动配置 MockMvc。Mock MVC 为快速测试 MVC 控制器提供了一种强大的方法,而无需启动完整的 HTTP 服务器。

你也可以在非@WebMvcTest(如 @SpringBootTest)中自动配置 MockMvc,方法是用 @AutoConfigureMockMvc 对其进行注解。下面的示例使用了 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 放在一个特殊的 "作用域 "中,以确保驱动程序在每次测试后退出,并注入一个新实例。如果不希望出现这种行为,可以在 WebDriver @Bean 定义中添加 @Scope("singleton")

Spring Boot 创建的 WebDriver 作用域将取代任何用户定义的同名作用域。如果您定义了自己的 WebDriver 作用域,可能会发现它在使用 @WebMvcTest 时停止工作。

如果类路径上有 Spring Security,@WebMvcTest 也会扫描 WebSecurityConfigurer Bean。你可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。有关如何使用 Spring Security 的 MockMvc 支持的更多详情,请参阅 [howto.html](https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.testing.with-spring-security) 如何使用部分。

有时,仅编写 Spring MVC 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试。

自动配置 Spring WebFlux 测试

要测试 Spring WebFlux 控制器是否按预期运行,可以使用 @WebFluxTest 注解。@WebFluxTest 会自动配置 Spring WebFlux 基础架构,并将扫描的 Bean 限制为 @Controller@ControllerAdvice@JsonComponentConverterGenericConverterWebFilterWebFluxConfigurer。使用 @WebFluxTest 注解时,常规的 @Component@ConfigurationProperties Bean 不会被扫描。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

@WebFluxTest 启用的自动配置列表见附录。

如果需要注册额外的组件(如 Jackson 模块),可以使用 @Import 在测试中导入额外的配置类。

通常,@WebFluxTest 仅限于单个控制器,并与 @MockBean 注解结合使用,为所需的协作者提供模拟实现。
@WebFluxTest 还能自动配置 [WebTestClient](https://docs.spring.io/spring-framework/reference/testing/webtestclient.html),这为快速测试 WebFlux 控制器提供了一种强大的方式,而无需启动完整的 HTTP 服务器。

你也可以在非 @WebFluxTest(如 @SpringBootTest)中自动配置 WebTestClient,方法是用 @AutoConfigureWebTestClient 对其进行注解。下面的示例展示了一个同时使用 @WebFluxTestWebTestClient 的类:

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 应用程序支持这种设置,因为在模拟网络应用程序中使用 WebTestClient 只能与 WebFlux 配合使用。

@WebFluxTest 无法检测通过功能性 Web 框架注册的路由。若要在上下文中测试 RouterFunction Bean,可考虑使用 @Import@SpringBootTest 自行导入 RouterFunction

@WebFluxTest 无法检测注册为 SecurityWebFilterChain 类型的 @Bean 的自定义安全配置。要将其纳入测试,需要使用 @Import@SpringBootTest 来导入注册 Bean 的配置。

有时,仅仅编写 Spring WebFlux 测试是不够的;Spring Boot 可以帮助您使用实际服务器运行完整的端到端测试。

自动配置 Spring GraphQL 测试

Spring GraphQL 提供专门的测试支持模块;您需要将其添加到您的项目中:

<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>
dependencies {
  testImplementation("org.springframework.graphql:spring-graphql-test")
  // Unless already present in the implementation configuration
  testImplementation("org.springframework.boot:spring-boot-starter-webflux")
}

该测试模块包含 GraphQlTester。测试中会大量使用该测试器,因此请务必熟悉使用。GraphQlTester 有多种变体,Spring Boot 会根据测试类型自动配置它们:

  • ExecutionGraphQlServiceTester 在服务器端执行测试,没有客户端,也没有传输工具
  • HttpGraphQlTester 使用连接到服务器的客户端执行测试,无论是否有实时服务器

通过 @GraphQlTest 注解,Spring Boot 可以帮助您测试 Spring GraphQL 控制器@GraphQlTest 会自动配置 Spring GraphQL 基础架构,而不涉及任何传输或服务器。这将扫描的 Bean 限制为 @ControllerRuntimeWiringConfigurerJsonComponentConverterGenericConverterDataFetcherExceptionResolverInstrumentationGraphQlSourceBuilderCustomizer。当使用 @GraphQlTest 注解时,常规的 @Component@ConfigurationProperties Bean 不会被扫描。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

@GraphQlTest 启用的自动配置列表见附录。

通常,@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!");
    }

}

自动配置 Data Cassandra 测试

您可以使用 @DataCassandraTest 测试 Cassandra 应用程序。默认情况下,它会配置 CassandraTemplate、扫描 @Table 类并配置 Spring Data Cassandra 资源库。使用 @DataCassandraTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Cassandra 的更多信息,请参阅 “data.html”)。

@DataCassandraTest 启用的自动配置设置列表见附录。

下面的示例展示了在 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;

}

自动配置Data Couchbase 测试

您可以使用 @DataCouchbaseTest 测试 Couchbase 应用程序。默认情况下,它会配置 CouchbaseTemplateReactiveCouchbaseTemplate、扫描 @Document 类并配置 Spring Data Couchbase 存储库。使用 @DataCouchbaseTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Couchbase 的更多信息,请参阅本章前面的 “data.html”)。

@DataCouchbaseTest 启用的自动配置设置列表见附录。

下面的示例展示了在 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;

    // ...

}

自动配置Data Elasticsearch 测试

您可以使用 @DataElasticsearchTest 测试 Elasticsearch 应用程序。默认情况下,它会配置 ElasticsearchRestTemplate、扫描 @Document 类并配置 Spring Data Elasticsearch 资源库。使用 @DataElasticsearchTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Elasticsearch 的更多信息,请参阅本章前面的 “data.html”)。

@DataElasticsearchTest 启用的自动配置设置列表见附录。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest;

@DataElasticsearchTest
class MyDataElasticsearchTests {

    @Autowired
    private SomeRepository repository;

    // ...

}

自动配置Data JPA 测试

您可以使用 @DataJpaTest 注解来测试 JPA 应用程序。默认情况下,它会扫描 @Entity 类并配置 Spring Data JPA 资源库。如果类路径上有嵌入式数据库,它也会配置一个。通过将 spring.jpa.show-sql 属性设置为 true,默认情况下会记录 SQL 查询。可以使用注解的 showSql 属性禁用此功能。
使用 @DataJpaTest 注解时不会扫描常规 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

@DataJpaTest 启用的自动配置设置列表见附录。

默认情况下,数据 JPA 测试是事务性的,并在每次测试结束时回滚。详情请参见 Spring Framework 参考文档中的相关章节。如果这不是你想要的,你可以为某个测试或整个类禁用事务管理,方法如下:

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](https://github.com/spring-projects/spring-boot/tree/v3.2.0/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/orm/jpa/TestEntityManager.java) Bean,它提供了标准 JPA EntityManager 的替代方案,是专门为测试设计的。

TestEntityManager 也可以通过添加 @AutoConfigureTestEntityManager 自动配置到任何基于 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 类似,但适用于只需要数据源而不使用 Spring Data JDBC 的测试。默认情况下,它会配置一个内存嵌入式数据库和一个 JdbcTemplate。使用 @JdbcTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

@JdbcTest 启用的自动配置列表见附录。

默认情况下,JDBC 测试是事务性的,会在每次测试结束时回滚。详情请参见 Spring Framework 参考文档中的相关章节。如果这不是你想要的,你可以禁用某个测试或整个类的事务管理,具体做法如下:

import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {

}

如果您希望您的测试针对真实数据库运行,您可以使用 @AutoConfigureTestDatabase 注解,方法与 @DataJpaTest 相同。(请参阅 “自动配置的Data JPA 测试”)。

自动配置Data JDBC 测试

@DataJdbcTest@JdbcTest 类似,但用于使用 Spring Data JDBC 资源库的测试。默认情况下,它会配置一个内存嵌入式数据库、一个 JdbcTemplate 和 Spring Data JDBC 资源库。使用 @DataJdbcTest 注解时,只会扫描 AbstractJdbcConfiguration 子类,而不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

附录中列出了 @DataJdbcTest 启用的自动配置。

默认情况下,Data JDBC 测试是事务性的,并在每次测试结束时回滚。有关详细信息,请参阅 Spring Framework 参考文档中的相关章节。如果这不是你想要的,你可以禁用某个测试或整个测试类的事务管理,如 JDBC 示例所示。
如果您希望您的测试针对真实数据库运行,您可以使用 @AutoConfigureTestDatabase 注解,方法与 @DataJpaTest 相同。(请参阅 “自动配置Data JPA 测试”)。

自动配置Data R2DBC 测试

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

@DataR2dbcTest 启用的自动配置列表见附录。

默认情况下,Data R2DBC 测试不是事务性的。
如果您希望测试在真实数据库中运行,可以使用 @AutoConfigureTestDatabase 注解,其使用方法与 @DataJpaTest 相同。(请参阅 “自动配置Data JPA 测试”)。

自动配置 jOOQ 测试

使用 @JooqTest 的方式与 @JdbcTest 类似,但用于与 jOOQ 相关的测试。由于 jOOQ 在很大程度上依赖于与数据库模式相对应的基于 Java 的模式,因此会使用现有的 DataSource。如果你想用内存数据库代替它,可以使用 @AutoConfigureTestDatabase 来覆盖这些设置。(使用 @JooqTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

@JooqTest 启用的自动配置列表见附录。

@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 示例所示。

自动配置Data MongoDB 测试

您可以使用 @DataMongoTest 测试 MongoDB 应用程序。默认情况下,它会配置 MongoTemplate、扫描 @Document 类并配置 Spring Data MongoDB 存储库。使用 @DataMongoTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 MongoDB 的更多信息,请参阅 “data.html”)。

@DataMongoTest 启用的自动配置设置列表见附录。

下面的类显示了 @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;

    // ...

}

自动配置Data Neo4j 测试

你可以使用 @DataNeo4jTest 测试 Neo4j 应用程序。默认情况下,它会扫描 @Node 类,并配置 Spring Data Neo4j 资源库。使用 @DataNeo4jTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Neo4J 的更多信息,请参阅 “data.html”)。

@DataNeo4jTest 启用的自动配置设置列表见附录。

下面的示例展示了在 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;

    // ...

}

默认情况下,Data Neo4j 测试是事务性的,并在每次测试结束时回滚。更多详情,请参阅 Spring Framework 参考文档中的相关章节。如果这不是你想要的,你可以禁用某个测试或整个类的事务管理,如下所示:

import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {

}

反应式访问不支持事务测试。如果使用这种风格,则必须如上所述配置 @DataNeo4jTest 测试。

自动配置Data Redis 测试

你可以使用 @DataRedisTest 测试 Redis 应用程序。默认情况下,它会扫描 @RedisHash 类并配置 Spring Data Redis 存储库。使用 @DataRedisTest 注解时,不会扫描常规的 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 Redis 的更多信息,请参阅 “data.html”)。

@DataRedisTest 启用的自动配置设置列表见附录。

下面的示例展示了 @DataRedisTest 注解的使用:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;

@DataRedisTest
class MyDataRedisTests {

    @Autowired
    private SomeRepository repository;

    // ...

}

自动配置Data LDAP 测试

您可以使用 @DataLdapTest 测试 LDAP 应用程序。默认情况下,它会配置内存中的嵌入式 LDAP(如果可用)、配置 LdapTemplate、扫描 @Entry 类并配置 Spring Data LDAP 资源库。使用 @DataLdapTest 注解时,不会扫描常规 @Component@ConfigurationProperties Bean。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。(有关在 Spring Boot 中使用 LDAP 的更多信息,请参阅 “data.html”)。

@DataLdapTest 启用的自动配置设置列表见附录。

下面的示例展示了 @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 的支持,配置 RestTemplateBuilderRestClient.Builder,并添加对 MockRestServiceServer 的支持。当使用 @RestClientTest 注解时,常规的 @Component@ConfigurationProperties Bean 不会被扫描。@EnableConfigurationProperties 可用于包含 @ConfigurationProperties Bean。

@RestClientTest 启用的自动配置设置列表见附录。

应使用 @RestClientTestvaluecomponents 属性指定要测试的特定 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"));
    }

}

如果您需要对 Spring REST 文档配置进行比 @AutoConfigureRestDocs 属性更多的控制,可以使用 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测试

在测试反应式 Web 应用程序时,@AutoConfigureRestDocs 还可以与 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 使预设为使用 Spring REST Docs的 RequestSpecification Bean 可供测试使用。您可以使用 @Autowired 注入该 Bean,并在测试中使用它,就像通常使用 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));
    }

}

如果您需要对 Spring REST 文档配置进行比 @AutoConfigureRestDocs 属性更多的控制,则可以使用 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 服务项目调用 Web 服务的应用程序。默认情况下,它会配置一个模拟 WebServiceServer Bean,并自动自定义 WebServiceTemplateBuilder。(有关使用 Spring Boot 的 Web 服务的更多信息,请参阅 “io.html”)。

@WebServiceClientTest 启用的自动配置设置列表见附录。

下面的示例展示了 @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 服务项目实现 Web 服务的应用程序。默认情况下,它会配置一个 MockWebServiceClient Bean,用于调用 Web 服务端点。(有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅 “io.html”)。

@WebServiceServerTest 启用的自动配置设置列表见附录。

下面的示例展示了 @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 注释的测试中启用。

您可以在此文件中使用带有 # 的注释。

只要使用 @ImportAutoConfiguration 对片段或 @AutoConfigure... 注释进行元注解,就可以通过这种方式对其进行自定义。

用户配置和切片

如果你的代码结构合理,你的 @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 类的更多详情,请参阅本节

测试片从扫描中排除 @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 应用程序。为此,请在应用程序的构建中添加对 -groovy-4.0 版本 Spock 的 spock-spring 模块的依赖关系。spock-spring 将 Spring 的测试框架集成到 Spock 中。更多详情,请参阅 Spock 的 Spring 模块文档

测试容器

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 来实现的,Neo4j 自动配置会使用该 Bean,并覆盖任何与连接相关的配置属性。

您需要将 spring-boot-testcontainers 模块添加为测试依赖项,以便使用 Testcontainers 服务连接。

服务连接注解由在 spring.factories 中注册的 ContainerConnectionDetailsFactory 类处理。ContainerConnectionDetailsFactory 可根据特定容器子类或 Docker 映像名称创建 ConnectionDetails Bean。
spring-boot-testcontainers jar 中提供了以下服务连接工厂:

Connection DetailsMatched on
ActiveMQConnectionDetails名为 "symptoma/activemq "的容器
CassandraConnectionDetailsCassandraContainer类型的容器
CouchbaseConnectionDetailsCouchbaseContainer类型的容器
ElasticsearchConnectionDetailsElasticsearchContainer类型的容器
FlywayConnectionDetailsJdbcDatabaseContainer类型的容器
JdbcConnectionDetailsJdbcDatabaseContainer类型的容器
KafkaConnectionDetailsKafkaContainerRedpandaContainer类型的容器
LiquibaseConnectionDetailsJdbcDatabaseContainer类型的容器
MongoConnectionDetailsMongoDBContainer类型的容器
Neo4jConnectionDetailsNeo4jContainer类型的容器
OtlpMetricsConnectionDetails名为 “otel/opentelemetry-collector-contrib” 的容器
OtlpTracingConnectionDetails名为 “otel/opentelemetry-collector-contrib” 的容器
PulsarConnectionDetailsPulsarContainer类型的容器
R2dbcConnectionDetails类型为 MariaDBContainerMSSQLServerContainerMySQLContainerOracleContainerPostgreSQLContainer 的容器
RabbitConnectionDetailsRabbitMQContainer类型的容器
RedisConnectionDetails名为 "redis "的容器
ZipkinConnectionDetails名为 "openzipkin/zipkin "的容器

默认情况下,将为给定容器创建所有适用的连接详情 Bean。例如,PostgreSQLContainer 将同时创建 JdbcConnectionDetailsR2dbcConnectionDetails
如果只想创建适用类型的子集,可以使用 @ServiceConnection 的 type 属性。

默认情况下,Container.getDockerImageName() 用于获取用于查找连接详细信息的名称。只要 Spring Boot 能够获取 Container 的实例(如上例中使用静态字段的情况),该方法就能正常工作。
如果使用 @Bean 方法,Spring Boot 不会调用 bean 方法来获取 Docker 映像名称,因为这会导致急于初始化的问题。相反,Bean 方法的返回类型会被用来查找应使用的连接细节。只要您使用的是类型化容器,例如 Neo4jContainerRabbitMQContainer,这种方法就会起作用。如果您使用的是 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 中得知使用了哪个容器映像,因此必须使用 @ServiceConnectionname 属性来提供该提示。
您也可以使用 @ServiceConnectionname 属性来覆盖将使用的连接细节,例如在使用自定义映像时。如果你使用的是 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 的环境中。要获得 @Value 支持,需要额外配置 PropertySourcesPlaceholderConfigurer 或使用 @SpringBootTest 自动配置。

TestPropertyValues

TestPropertyValues 可让您快速向 ConfigurableEnvironmentConfigurableApplicationContext 添加属性。您可以使用 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");
    }

}

OutputCapture

OutputCapture 是一个 JUnit 扩展,可用于捕获 System.outSystem.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 的便捷替代品,在集成测试中非常有用。你可以获得一个普通模板或一个发送 Basic HTTP 身份验证(带用户名和密码)的模板。无论哪种情况,模板都是容错的。这意味着它不会在出现 4xx 和 5xx 错误时抛出异常,从而以测试友好的方式运行。相反,可以通过返回的 ResponseEntity 及其状态代码检测到此类错误。

TestRestTemplate 是 Spring RestTemplate 的便捷替代品,在集成测试中非常有用。你可以获得一个普通模板或一个发送 Basic HTTP 身份验证(带用户名和密码)的模板。无论哪种情况,模板都是容错的。这意味着它不会在出现 4xx 和 5xx 错误时抛出异常,从而以测试友好的方式运行。相反,可以通过返回的 ResponseEntity 及其状态代码检测到此类错误。

建议使用 Apache HTTP 客户端(5.1 或更高版本),但并非必须。如果您的类路径上有 Apache HTTP 客户端,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");
    }

}

另外,如果使用带有 WebEnvironment.RANDOM_PORTWebEnvironment.DEFINED_PORT@SpringBootTest 注解,就可以注入一个完全配置好的 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));
        }

    }

}

文章来源:https://blog.csdn.net/qq_41939901/article/details/135009418
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。