SpringBootTest 测试工具

SpringBootTest 测试工具

如下内容,翻译自官方文档,并结合了学习过程的demo。html


Spring Boot提供了许多实用程序和注解,帮助测试应用程序。测试支持由两个模块提供: spring-boot-test 包含核心项, spring-boot-test-autoconfigure 支持测试的自动配置。

大多数开发人员使用 spring-boot-starter-test,它同时导入 SpringBoot 测试模块以及JUnit Jupiter、AssertJ、Hamcrest和许多其余有用的库。java

此文使用当前最新稳定版本: SpringBoot 2.2.2.RELEASE
此 starter 还带来了 vintage  引擎,所以能够同时运行JUnit 4和JUnit 5测试。若是已经将测试迁移到JUnit5,那么应该排除JUnit4支持,以下例所示:react

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <exclusions>
        <exclusion>
          <!-- 此模块兼容junit4 和 junit 5,此示例直接使用 junit5 -->
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
      <scope>test</scope>
    </dependency>

Test Scope Dependencies

spring-boot-starter-test (依赖 scopetest)包含如下库:git

  • JUnit 5:包含兼容 JUnit 4,Java 应用程序单元测试的事实标准
  • Spring Test 和 SpringBootTest:对Spring Boot应用程序的公共和集成测试支持。
  • AssertJ:流式断言库
  • Hamcrest:匹配对象库
  • Mockito:Java 模拟框架
  • JSONassert:JSON 断言库
  • JsonPath:JSON XPath

测试 Spring 应用

依赖注入的一个主要优势是它应该使代码更容易进行单元测试。可使用新的操做符实例化对象,甚至不涉及Spring。也可使用模拟对象而不是实际依赖项。

一般,您须要超越单元测试,开始集成测试(使用Spring ApplicationContext)。可以在不须要部署应用程序或链接到其余基础结构的状况下执行集成测试是很是有用的。github

Spring框架包含了一个专门的集成测试模块。能够直接向 org.springframework:spring 测试声明一个依赖项,或者使用 spring-boot-starter-testweb

若是之前没有使用过 spring-test 模块,那么应该从阅读spring框架参考文档的相关部分开始。redis

测试 SpringBoot 应用

SpringBoot 应用程序是 Spring ApplicationContext,所以除了使用普通的Spring上下文以外,没必要作任何特别的事情来测试它。算法

默认状况下,只有在使用 SpringApplication 建立 Spring Boot时,它的外部属性、日志记录和其余特性才会安装在上下文中。spring

SpringBoot 提供了一个 @SpringBootTest 注解,当须要SpringBoot 特性时,它能够做为标准 spring-test @ContextConfiguration 注解的替代。注解的工做方式是经过 SpringApplication 建立测试中使用的ApplicationContext。除了 @SpringBootTest以外,还提供了一些其余注解,用于测试应用程序的更具体的部分。数据库

若是使用的是JUnit4,不要忘记将 @RunWith(SpringRunner.class) 添加到测试中,不然注解将被忽略。若是使用的是JUnit5,则不须要添加与 @SpringBootTest 和 其余已经使用的注解等效的 @ExtendWith(SpringExtension.class)

默认状况下,@SpringBootTest 不会启动服务器。可使用 @SpringBootTestwebEnvironment 属性进一步优化测试的运行方式:

  • MOCK(默认):加载 web ApplicationContext 并提供模拟web环境。使用此注解时,嵌入式服务器未启动。若是类路径上没有可用的web环境,则此模式会透明地回退到建立常规的非web ApplicationContext。它能够与 @AutoConfigureMockMvc@AutoConfigureWebTestClient 结合使用,对web应用程序进行基于模拟的测试。
  • RANDOM_PORT:加载 WebServerApplicationContext 并提供真正的web环境。嵌入式服务器启动并在随机端口上监听。
  • DEFINED_PORT:加载 WebServerApplicationContext 并提供真正的web环境。嵌入式服务器将启动并在定义的端口(从 application.properties)或默认端口8080上监听。
  • NONE:使用 SpringApplication 加载 ApplicationContext,但不提供任何web环境(mock或其余)。

若是测试是 @Transactional,那么默认状况下,它会在每一个测试方法结束时回滚事务。然而,因为对随机端口或定义的端口使用这种安排隐式地提供了一个真正的servlet环境,HTTP客户机和服务器在单独的线程中运行,所以在单独的事务中运行。在这种状况下,服务器上启动的任何事务都不会回滚。
@使用 webEnvironment=webEnvironment.RANDOM_PORT@SpringBootTest 也将在单独的随机端口上启动管理服务器,若是应用程序对管理服务器使用不一样的端口。

检测 Web 应用类型

若是 Spring MVC 可用,那么将配置一个常规的基于MVC的应用程序上下文。若是只有Spring WebFlux,将检测并配置基于 WebFlux 的应用程序上下文。

若是二者都存在,则以Spring MVC为准。若是要在此方案中测试响应式web应用程序,则必须设置 spring.main.web-application-type 属性:

@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests { ... }

检测测试配置

Spring 测试框架中,可使用 @ContextConfiguration(classes=…) 来指定加载那个 Spring @Configuration

在测试 Spring Boot 应用程序时,这一般不是必需的。Spring Boot的 @Test 类注解在没有显式定义主配置时自动搜索主配置。

搜索算法从包含测试的包开始工做,直到找到用 @SpringBootApplication@SpringBootConfiguration 注解的类为止。只要以合理的方式构造代码,一般都会找到主配置。

若是要自定义主配置,可使用嵌套的 @TestConfiguration 类。与嵌套的 @Configuration 类(将用于替代应用程序的主配置)不一样,嵌套的 @TestConfiguration 类是在应用程序的主配置额外使用的。

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

排除测试配置

若是使用了 @SpringBootApplication@ComponentScan 扫描,针对特定测试的顶级配置类可能在任何地方都可以被获取到。

如前所述,@TestConfiguration 可用于测试的内部类,以自定义主要配置。当放置在顶级类上时,@TestConfiguration 表示 src/test/java 中的类不该该经过扫描来获取。而后,能够在须要时显式导入该类,以下例所示:

@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
    @Test
    void exampleTest() {
        //... 
    }
}

使用应用参数

若是应用程序须要参数,可使用 @SpringBootTestargs 属性注入它们。

@SpringBootTest(args = "--app.name=test", webEnvironment = WebEnvironment.NONE)
public class ArgTests {

  @Test
  public void applicationArgsTest(@Autowired ApplicationArguments args) {
    assertThat(args.getOptionNames()).containsOnly("app.name");
    assertThat(args.getOptionValues("app.name")).containsOnly("test");
  }
}

用 mock environment测试

默认状况下,@SpringBootTest 不会启动服务器。若是在此模拟环境中有要测试的web端点,则能够另外配置MockMvc,以下例所示:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
public interface ConstantUtil {
  String body = "Hello World!";
}
@RestController
public class HelloWorldController {

  @RequestMapping
  public String helloWorld(){
    return ConstantUtil.body;
  }
}
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcTests {
  @Test
  public void test(@Autowired MockMvc mvc) throws Exception {
    mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string(ConstantUtil.body));
  }
}
若是您只想关注web层而不想启动一个完整的 ApplicationContext,能够考虑改用 @WebMvcTest

另外,能够配置 WebTestClient ,以下:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
/**
 * WebTestClient<br>
 * WebTestClient 属于 reactive,设置spring.main.web-application-type=reactive
 */
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
@AutoConfigureWebTestClient
public class MockWebTestClientTests {
  @Test
  public void test(@Autowired WebTestClient webTestClient) {
    webTestClient
        .get()
        .uri("/")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo(ConstantUtil.body);
  }
}
在模拟环境中进行测试一般比使用完整的Servlet容器运行要快。可是,因为模拟发生在Spring MVC层,依赖于低级Servlet容器行为的代码不能直接用MockMvc测试。
例如,Spring Boot的错误处理基于Servlet容器提供的“错误页”支持。这意味着,虽然能够按预期测试MVC层抛出和处理异常,但不能直接测试是否呈现了特定的自定义错误页。若是须要测试这些较低级别的关注点,能够按照下一节中的说明启动彻底运行的服务器。

用运行中的server测试

若是须要启动彻底运行的服务器,建议使用随机端口。若是使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT),则每次运行测试时都会随机选择一个可用端口。

@LocalServerPort 注解可用于将实际使用的端口注入测试。为了方便起见,须要对启动的服务器进行REST调用的测试还能够 @Autowire 一个 WebTestClient,它解析到正在运行的服务器的相关连接,并附带用于验证响应的专用API,以下例所示:

/**
 * 使用运行中的server<br>
 * 使用 webflux
 *
 * @author YiFeiXi
 */
@SpringBootTest(
    webEnvironment = WebEnvironment.RANDOM_PORT,
    properties = "spring.main.web-application-type=reactive")
public class RandomPortWebTestClientTests {
  @Test
  public void test(@Autowired WebTestClient webTestClient) {
    webTestClient
        .get()
        .uri("/")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo(ConstantUtil.body);
  }
}

此设置须要类路径上的 spring-webflux。若是您不能或不想添加webflux,Spring Boot 还提供了一个 TestRestTemplate 工具:

/**
 * 使用运行中的server<br>
 * 不使用 webflux
 *
 * @author YiFeiXi
 */
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RandomPortTestRestTemplateTests {
  @Test
  public void test(@Autowired TestRestTemplate restTemplate) {
    String body = restTemplate.getForObject("/", String.class);
    assertThat(body).isEqualTo(ConstantUtil.body);
  }
}

自定义 WebTestClient

要自定义 WebTestClient bean,需配置 WebTestClientBuilderCustomizer bean。使用用于建立 WebTestClientWebTestClient.Builder 调用任何此类bean。

使用 JMX

因为测试上下文框架缓存上下文的缘由,默认状况下禁用JMX以防止相同的组件在同一域上注册。若是此类测试须要访问 MBeanServer,请考虑将其标记为dirty :

@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class SampleJmxTests {
    @Autowired
    private MBeanServer mBeanServer;
    @Test
    void exampleTest() {
        // ...
    }
}

虚拟和监控 Beans

运行测试时,有时须要在应用程序上下文中模拟某些组件。例如,可能有一个表面覆盖了一些在开发期间不可用的远程服务。当您但愿模拟在实际环境中可能难以触发的故障时,模拟也颇有用。

Spring Boot包含一个 @MockBean 注解,能够用来为 ApplicationContext 中的 bean 定义Mockito 模拟。可使用注解添加新bean或替换单个现有bean定义。注解能够直接用于测试类、测试中的字段或 @Configuration 类和字段。在字段上使用时,建立的模拟的实例也会被注入。模拟bean在每一个测试方法以后自动重置。

若是测试使用了Spring Boot的一个测试注解(例如 @SpringBootTest),则会自动启用此功能。要将此功能与其余排列一块儿使用,必须显式添加 listener,以下例所示:

@TestExecutionListeners(MockitoTestExecutionListener.class)

下面的示例用模拟实现替换现有的 testService bean:

@SpringBootTest
public class MockBeanTests {
  @Autowired private TestController testController;
  @MockBean private TestService testService;

  @Test
  public void test(){
    given(testService.hello()).willReturn("哈哈");
    String hello = testController.hello();
    assertThat(hello).isEqualTo("哈哈");
  }
}

@MockBean 不能用于模拟在应用程序上下文刷新期间执行的bean的行为。在执行测试时,应用程序上下文刷新已经完成,如今配置模拟行为已经太晚了。咱们建议在这种状况下使用 @Bean 方法来建立和配置mock。

另外,您可使用 @SpyBean 来使用 Mockito spy 以包装任何现有的bean

CGLIB代理,如为范围bean建立的代理,将代理方法声明为 final。这会阻止Mockito正常工做,由于它没法在默认配置中模拟或监视 final 方法。若是想模拟或监视这样的bean,能够经过将 org.mockito:mockito-inline 添加到应用程序的测试依赖项中来配置 Mockito 以使用其内联mock maker。这容许Mockito模拟和监视 final 方法。

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

若是使用 @SpyBean 监视具备按名称引用参数的 @Cacheable 方法的bean,则必须使用 -parameters 编译应用程序。这确保了一旦bean被监视,参数名就可用于缓存基础结构。

自动配置测试

Spring Boot的自动配置系统对应用程序运行良好,但有时对测试来讲可能有点太多了。它一般有助于只加载测试应用程序“片断”所需的配置部分。例如,您可能但愿测试Spring MVC控制器是否正确映射了url,而且您不但愿在这些测试中涉及数据库调用,或者您可能但愿测试JPA实体,而且您对运行这些测试时的web层不感兴趣。

spring-boot-test-autoconfigure 模块包括许多注解,能够用来自动配置这些“片断”。它们中的每个都以相似的方式工做,提供一个加载 ApplicationContext@…Test 注解和一个或多个可用于自定义自动配置设置的 @AutoConfigure… 注解。

每一个片断将组件扫描限制为适当的组件,并加载一组很是有限的自动配置类。若是须要排除其中一个,大多数 @…Test 注解都提供 excludeAutoConfiguration 属性。或者,可使用 @ImportAutoConfiguration#exclude

不支持在一个测试类中经过使用多个 @...Test 注解来包含多个“片断”。若是须要多个“片断”,请选择其中一个 @…Test 注解并手动包含其余“片断”的 @AutoConfigure… 注解。

也能够将 @AutoConfigure… 注解与标准的 @SpringBootTest 注解一块儿使用。若是对应用程序的“片断”不感兴趣,但须要一些自动配置的测试bean,则可使用此组合。

自动配置 JSON 测试

要测试对象JSON序列化和反序列化是否按预期工做,可使用 @JsonTest 注解。@JsonTest 自动配置可用的受支持JSON mapper,该 mapper 能够是如下库之一:

  • Jackson ObjectMapper,任何 @JsonComponent bean 和 任何 Jackson Module
  • Gson
  • Jsonb

能够在附录中找到@JsonTest启用的自动配置列表。

若是须要配置自动配置的元素,可使用 @AutoConfigureJsonTesters 注解。

Spring Boot包括基于AssertJ的辅助程序,它们与 JSONAssert 和 JsonPath 库一块儿工做,检查JSON是否如预期的那样出现。JacksonTesterGsonTesterJsonbTesterBasicJsonTester 类能够分别用于Jackson、Gson、Jsonb和字符串。使用 @JsonTest 时,测试类上的任何辅助字段均可以 @Autowired。如下示例显示了Jackson的测试类:

/** @author YiFeiXi */
@JsonTest
public class JsonTests {

  @Autowired private JacksonTester<UserInfo> json;

  @Test
  public void testSerialize() throws Exception {
    UserInfo u = new UserInfo(1, "张", "三");
    assertThat(this.json.write(u))
        .isEqualToJson("{\"id\":1,\"firstName\":\"张\",\"lastName\": \"三\"}");
  }

  @Test
  public void testDeserialize() throws Exception {
    String content = "{\"firstName\":\"张\",\"lastName\": \"三\"}";
    assertThat(this.json.parseObject(content).getFirstName()).isEqualTo("张");
  }
}

JSON辅助类也能够直接用于标准单元测试。为此,若是不使用 @JsonTest,请在 @Before 方法中调用辅助类的 initFields 方法。

若是使用Spring Boot的基于AssertJ的辅助程序来 assert 给定JSON路径上的数值,则可能没法根据类型使用isEqualTo。相反,您可使用 AssertJ 的 satisfies 来 assert 该值与给定条件匹配。例如,下面的示例 assert 实际数字是偏移量0.01内接近0.15的浮点值。

assertThat(json.write(message))
    .extractingJsonPathNumberValue("@.test.numberValue")
    .satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));

自动配置 Spring MVC 测试

要测试Spring MVC controllers 是否按预期工做,使用 @WebMvcTest 注解。@WebMvcTest 自动配置Spring MVC基础结构,并将扫描的bean限制为@Controller、@ControllerAdvice、@JsonComponent、Converter、GenericConverter、Filter、HandlerInterceptor、WebMVCConfiguer和HandlerMethodArgumentResolver。使用此注解时,不扫描常规@Component bean。

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

若是须要注册额外的组件,好比Jackson模块,能够在测试中使用@import导入额外的配置类。
一般,@WebMvcTest 仅限于一个控制器,并与 @MockBean 结合使用,为所需的协做者提供模拟实现。
@WebMvcTest还自动配置MockMvc。Mock MVC提供了一种快速测试MVC控制器的强大方法,无需启动完整的HTTP服务器。

还能够在非@WebMvcTest(如@SpringBootTest)中使用 @AutoConfigureMockMvc 对MockMvc进行自动配置。如下示例使用MockMvc:

/** @author YiFeiXi */
@WebMvcTest(TestController.class)
public class WebMvcTests {
  @Autowired private MockMvc mvc;
  @MockBean private TestService testService;

  @Test
  public void test() throws Exception {
    given(this.testService.hello()).willReturn("哈哈");
    this.mvc
        .perform(get("/hello").accept(MediaType.TEXT_PLAIN))
        .andExpect(status().isOk())
        .andExpect(content().string("哈哈"));
  }
}

若是须要配置自动配置的元素(例如,当应用servlet过滤器时),可使用 @AutoConfigureMockMvc 注解中的属性。

若是使用HtmlUnit或Selenium,自动配置还提供HtmlUnit WebClient bean 或 Selenium WebDriver bean。如下示例使用HtmlUnit:

@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {

    @Autowired
    private WebClient webClient;
    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot"))
                .willReturn(new VehicleDetails("Honda", "Civic"));
        HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
        assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
    }
}

默认状况下,Spring Boot 将 WebDriver bean 放置在一个特殊的“scope”中,以确保在每次测试以后驱动程序退出,并注入新的实例。若是不但愿出现这种行为,能够将 @Scope("singleton") 添加到 WebDriver @Bean 定义中。

由Spring Boot建立的 webDriver 做用域将替换任何用户定义的同名做用域。若是定义了本身的webDriver做用域,则在使用 @WebMvcTest 时可能会发现它中止工做。

若是类路径上有Spring Security ,@WebMvcTest 还将扫描 WebSecurityConfigurer bean。可使用Spring Security的测试支持,而不是彻底禁用此类测试的安全性。有关如何使用Spring Security的 MockMvc 支持的更多详细信息,请参见 SpringSecurity 测试操做步骤部分。

自动配置 Spring WebFlux 测试

要测试Spring WebFlux controllers 是否按预期工做,可使用 @WebFluxTest 注解。@WebFluxTest 自动配置Spring WebFlux基础结构,并将扫描的bean限制为@Controller、@ControllerAdvice、@JsonComponent、Converter、GenericConverter、WebFilter和WebFluxConfigurer。使用@WebFluxTest注解时,不扫描常规@Component bean。

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

若是须要注册额外的组件,好比Jackson模块,能够在测试中使用@import导入额外的配置类。
一般,@WebFluxTest 仅限于一个控制器,并与@MockBean注解结合使用,为所需的协做者提供模拟实现。
@WebFluxTest还能够自动配置WebTestClient,这为快速测试WebFlux controllers提供了一种强大的方法,而无需启动完整的HTTP服务器。
还能够在非@WebFluxTest(如@SpringBootTest)中使用@AutoConfigureWebTestClient 注解,从而自动配置web测试客户端。如下示例显示同时使用@WebFluxTest和WebTestClient的类:

/**
 * WebTestClient<br>
 * WebTestClient 属于 reactive,设置spring.main.web-application-type=reactive
 */
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
@AutoConfigureWebTestClient
public class MockWebTestClientTests {
  @Test
  public void test(@Autowired WebTestClient webTestClient) {
    webTestClient
        .get()
        .uri("/")
        .exchange()
        .expectStatus()
        .isOk()
        .expectBody(String.class)
        .isEqualTo(ConstantUtil.body);
  }
}

此设置仅受WebFlux应用程序支持,由于在模拟的web应用程序中使用WebTestClient目前仅适用于WebFlux。

@WebFluxTest没法检测经过功能性web框架注册的路由。要在上下文中测试 RouterFunction bean,请考虑经过@Import或使用@SpringBootTest亲自导入RouterFunction。

@WebFluxTest没法检测经过 SecurityWebFilterChain 类型的@Bean注册的自定义安全配置。要将其包含在测试中,须要经过@import或使用@SpringBootTest导入注册bean的配置。

自动配置 Data JPA 测试

可使用 @DataJpaTest 注解来测试JPA应用程序。默认状况下,它扫描@Entity类并配置Spring Data JPA repositories。若是类路径上有可用的嵌入式数据库,它也会配置一个。常规@Component bean不会加载到ApplicationContext中。

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

默认状况下,Data JPA 测试是事务性的,并在每一个测试结束时回滚。有关更多详细信息,请参阅Spring框架参考文档中的 相关部分。若是这不是你想要的,能够禁用测试或整个类的事务管理,以下所示:
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class ExampleNonTransactionalTests {

}

Data JPA 测试还能够注入一个 TestEntityManager bean,它提供了一个专门为测试设计的标准 JPA EntityManager 的替代。若是要在 @DataJpaTest 实例以外使用TestEntityManager,还可使用 @AutoConfigureTestEntityManager 注解。若是须要,还可使用 JdbcTemplate。如下示例使用了@DataJpaTest注解:

<!-- 内存嵌入式数据库 -->
    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derby</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
/** @author YiFeiXi */
@DataJpaTest
public class DataJpaTests {
  @Autowired private TestEntityManager entityManager;
  @Autowired private UserRepository userRepository;

  @Test
  public void find() throws Exception {
    this.entityManager.persist(new UserInfo(1, "张", "三"));
    UserInfo user = this.userRepository.findByFirstName("张");
    assertThat(user.getLastName()).isEqualTo("三");
  }
}

内存嵌入式数据库一般能够很好地用于测试,由于它们速度快,不须要任何安装。可是,若是但愿对真实数据库运行测试,则可使用 @AutoConfigureTestDatabase 注解,以下例所示:

@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
class ExampleRepositoryTests {

    // ...

}

自动配置 JDBC 测试

@JdbcTest 相似于 @DataJpaTest,但只用于只须要 DataSource 而不使用 Spring Data JDBC 的测试。默认状况下,它配置内存嵌入式数据库和 JdbcTemplate。常规@Component bean不会加载到ApplicationContext中。

@Slf4j
@JdbcTest
public class JdbcTests {
  @Autowired private JdbcTemplate jdbcTemplate;

  @Test
  void test() {
    jdbcTemplate.execute(
        "create table user_info(id int primary key, first_name varchar(20), last_name varchar(20))");
    jdbcTemplate.execute("insert into user_info(id, first_name, last_name) values(1,'张','三')");
    Map<String, Object> user = jdbcTemplate.queryForMap("select * from user_info where id = ?", 1);
    log.info("{} -> {}", user.get("first_name"), user.get("last_name"));
    assertThat(user.get("last_name")).isEqualTo("三");
  }
}

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

默认状况下,JDBC测试是事务性的,并在每一个测试结束时回滚。有关更多详细信息,请参阅Spring框架参考文档中的 相关部分。若是这不是您想要的,您能够为测试或整个类禁用事务管理,以下所示:
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class ExampleNonTransactionalTests {}

若是您但愿在真实数据库上运行测试,可使用 @AutoConfigureTestDatabase 注解,方法与对 DataJpaTest 的方法相同。

自动配置 Data JDBC 测试

@DataJdbcTest 相似于 @JdbcTest,但用于使用 Spring Data JDBC repositories 的测试。默认状况下,它配置内存嵌入式数据库、JdbcTemplate 和 Spring Data JDBC repositories。常规@Component bean不会加载到ApplicationContext中。

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

默认状况下,Data JDBC 测试是事务性的,并在每一个测试结束时回滚。若是这不是您想要的,您能够为测试或整个测试类禁用事务管理,如JDBC示例所示。

若是但愿在真实数据库上运行测试,可使用 @AutoConfigureTestDatabase 注解,方法与对DataJpaTest的方法相同。

自动配置 JOOQ 测试

自动配置 MongoDB 测试

自动配置 Neo4j 测试

自动配置 Redis 测试

可使用 @DataRedisTest 来测试Redis应用程序。默认状况下,它扫描 @RedisHash 类并配置Spring Data Redis repositories。常规@Component bean不会加载到ApplicationContext中。(有关在Spring Boot中使用Redis的更多信息,请参阅本章前面的“ Redis”。)

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

如下示例展现了 @DataRedisTest 的使用

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
/** @author YiFeiXi */
@DataRedisTest
public class DataRedisTests {
  @Autowired StringRedisTemplate stringRedisTemplate;

  @Test
  void test() {
    stringRedisTemplate.opsForValue().set("sex", "girl");
  }

  @Test
  void valueHasSet() {
    assertThat(stringRedisTemplate.opsForValue().get("sex")).isEqualTo("girl");
  }
}

自动配置 LDAP 测试

自动配置 Rest Clients

可使用 @RestClientTest 注解来测试 REST clients。默认状况下,它自动配置Jackson、GSON和Jsonb支持,配置 RestTemplateBuilder,并添加对 MockRestServiceServer 的支持。常规@Component bean不会加载到ApplicationContext中。

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

应该使用 @RestClientTestvaluecomponents 属性指定要测试的特定bean,以下例所示:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
/** @author YiFeiXi */
@RestClientTest
public class RestClientTests {
  private MockRestServiceServer server;
  @Autowired private RestTemplateBuilder restTemplateBuilder;
  private TestService testService;

  @BeforeEach
  void before() {
    RestTemplate restTemplate = restTemplateBuilder.build();
    testService = new TestService(restTemplate);
    server = MockRestServiceServer.createServer(restTemplate);
  }

  @Test
  void test() {
    server.expect(requestTo("/greet")).andRespond(withSuccess("suc", MediaType.TEXT_PLAIN));
    assertThat(testService.restReq()).isEqualTo("suc");
  }
}

自动配置 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中的host、
scheme 和 port 。

<dependency>
      <groupId>org.springframework.restdocs</groupId>
      <artifactId>spring-restdocs-mockmvc</artifactId>
      <scope>test</scope>
    </dependency>
/** @author YiFeiXi */
@WebMvcTest(HelloWorldController.class)
@AutoConfigureRestDocs(outputDir = "target/generated-snippets")
public class RestDocsTests {
  @Autowired private MockMvc mockMvc;

  @Test
  void hello() throws Exception {
    this.mockMvc
        .perform(get("").accept(MediaType.TEXT_PLAIN))
        .andExpect(status().isOk())
        .andDo(document("list-users"));
  }
}

用 Mock MVC 自动配置 Spring REST Docs

@AutoConfigureRestDocs 自定义 MockMvc bean 以使用 Spring REST Docs。可使用 @Autowired 注入它,并在测试中使用它,就像使用Mock MVC和Spring REST Docs 时同样,以下例所示:

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class UserDocumentationTests {

    @Autowired
    private MockMvc mvc;

    @Test
    void listUsers() throws Exception {
        this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
                .andExpect(status().isOk())
                .andDo(document("list-users"));
    }
}

若是须要比 @AutoConfigureRestDocs 的属性更多地控制Spring REST Docs配置,可使用 RestDocksMockMvcConfigurationCustomizer bean,以下例所示:

@TestConfiguration
static class CustomizationConfiguration
        implements RestDocsMockMvcConfigurationCustomizer {

    @Override
    public void customize(MockMvcRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}

若是想使用Spring REST Docs对参数化输出目录的支持,能够建立 RestDocumentationResultHandler bean。自动配置使用此结果处理程序调用 alwaysDo,从而使每一个 MockMvc 调用自动生成默认代码段。如下示例显示了被定义的RestDocumentationResultHandler

@TestConfiguration(proxyBeanMethods = false)
static class ResultHandlerConfiguration {

    @Bean
    public RestDocumentationResultHandler restDocumentation() {
        return MockMvcRestDocumentation.document("{method-name}");
    }

}

使用 WebTestClient 自动配置 Spring REST Docs

@AutoConfigureRestDocs 也能够与 WebTestClient 一块儿使用。可使用 @Autowired 注入它,并在测试中使用它,就像使用 @WebFluxTest 和 Spring REST Docs 时同样,以下例所示:

@WebFluxTest
@AutoConfigureRestDocs
class UsersDocumentationTests {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void listUsers() {
        this.webTestClient.get().uri("/").exchange().expectStatus().isOk().expectBody()
                .consumeWith(document("list-users"));
    }

}

若是须要比 @AutoConfigureRestDocs 的属性更多地控制Spring REST Docs配置,可使用 RestDocsWebTestClientConfigurationCustomizer bean,以下例所示:

@TestConfiguration(proxyBeanMethods = false)
public static class CustomizationConfiguration implements RestDocsWebTestClientConfigurationCustomizer {

    @Override
    public void customize(WebTestClientRestDocumentationConfigurer configurer) {
        configurer.snippets().withEncoding("UTF-8");
    }

}

使用 REST Assured 自动配置 Spring REST Docs

@AutoConfigureRestDocs 使一个 RequestSpecificatioin bean(预配置为使用Spring REST Docs)可用于您的测试。您可使用 @Autowired 注入它,并像使用 REST-Assured 和 Spring REST Docs 时同样在测试中使用它,以下例所示:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class UserDocumentationTests {

    @Test
    void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
        given(documentationSpec).filter(document("list-users")).when().port(port).get("/").then().assertThat()
                .statusCode(is(200));
    }

}

若是须要比 @AutoConfigureRestDocs 的属性更多地控制Spring REST Docs配置,那么可使用 RestDocsRestAssuredConfigurationCustomizer bean,以下例所示:

@TestConfiguration(proxyBeanMethods = false)
public static class CustomizationConfiguration implements RestDocsRestAssuredConfigurationCustomizer {

    @Override
    public void customize(RestAssuredRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}

额外的自动配置和片断

每一个片断提供一个或多个 @AutoConfigure… 注解,即定义应该做为片断一部分包含的自动配置。能够经过建立自定义 @AutoConfigure… 注解或向测试添加 @ImportAutoConfiguration 来添加其余自动配置,以下例所示:

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class ExampleJdbcTests {}

请确保不要使用常规的@Import 注解来导入自动配置,由于它们是由Spring Boot以特定方式处理的。

用户配置和片断

若是以合理的方式构造代码,那么默认状况下,@SpringBootApplication 类将用做测试的配置。
所以,重要的是不要在应用程序的主类中添加特定于其功能特定区域的配置设置。
假设使用的是Spring Batch,而且依赖于它的自动配置。能够按以下方式定义 @SpringBootApplication

@SpringBootApplication
@EnableBatchProcessing
public class SampleApplication {
    //...
}

由于这个类是测试的源配置,因此任何片断测试实际上都会尝试启动Spring Batch,这绝对不是您想要作的。建议的方法是将特定于区域的配置移动到与应用程序处于同一级别的单独 @Configuration 类,以下例所示:

@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class BatchConfiguration {
    //...
}

根据应用程序的复杂性,您能够为您的自定义设置一个单独的 @Configuration 类,也能够为每一个域区域设置一个类。后一种方法容许您在一个测试中启用它,若是须要,可使用@Import 注解。

测试片断从扫描中排除 @Configuration 类。例如,对于 @WebMvcTest,如下配置不会在测试片断加载的应用程序上下文中包含给定的 WebMvcConfigurer bean:

@Configuration
public class WebConfiguration {
    @Bean
    public WebMvcConfigurer testConfigurer() {
        return new WebMvcConfigurer() {
            //...
        };
    }
}

可是,下面的配置将致使测试片断加载自定义 WebMvcConfiguer

@Component
public class TestWebMvcConfigurer implements WebMvcConfigurer {
    //...
}

另外一个混乱的来源是类路径扫描。假设,当您以合理的方式构造代码时,您须要扫描另外一个包。您的应用程序可能相似于如下代码:

@SpringBootApplication
@ComponentScan({"com.example.app", "org.acme.another"})
public class SampleApplication{
    //...
}

这样作有效地覆盖了默认的组件扫描指令,其反作用是扫描这两个包,而不考虑您选择的片断。例如,@DataJpaTest 彷佛忽然扫描应用程序的组件和用户配置。一样,将自定义指令移动到单独的类是解决此问题的好方法。
若是这不适合,能够在测试层次结构中的某个位置建立@SpringBootConfiguration,以便使用它。或者,能够为测试指定一个 source,这将禁用查找默认源的行为。

使用 Spock 测试 SpringBoot 应用

若是但愿使用Spock测试一个Spring Boot 应用,那么应该在应用程序的构建中添加对Spock的 spock-spring 模块的依赖。spock-spring 将Spring的测试框架集成到 Spock 中。建议使用Spock 1.2或更高版本,以从Spock的Spring框架和SpringBoot集成的许多改进中获益。有关更多详细信息,请参阅Spock的Spring模块的文档

test-auto-configuration 附录

SpringBoot 测试注解附录

测试实用程序

当测试应用程序类打包为spring boot的一部分时,一些在测试应用程序一般是有用的。

ConfigFileApplicationContextInitializer

ConfigFileApplicationContextInitializer 是一个 ApplicationContextInitializer,能够将其应用于测试加载 Spring Boot application.properties 文件。当不须要@SpringBootTest提供的全套功能时,可使用它,以下例所示:

@ContextConfiguration(classes = Config.class,
    initializers = ConfigFileApplicationContextInitializer.class)

单独使用 ConfigFileApplicationContextInitializer 不支持 @Value(${…}) 注入。它的惟一工做是确保 application.properties 文件加载到Spring的环境中。对于 @Value 支持,须要另外配置 PropertySourcesPlaceholderConfigurer 或使用 @SpringBootTest,后者会自动配置一个。

/** @author YiFeiXi */
@Configuration
public class UserConfig {

  @Bean
  public UserInfo defaultUserInfo() {
    return new UserInfo(1, "张", "三");
  }
}
/** @author YiFeiXi */
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
    classes = UserConfig.class,
    initializers = ConfigFileApplicationContextInitializer.class)
public class ConfigFileTests {
  @Autowired private UserInfo userInfo;

  @Test
  void test() {
    assertThat(userInfo.getFirstName()).isEqualTo("张");
    assertThat(userInfo.getLastName()).isEqualTo("三");
  }
}

TestPropertyValues

TestPropertyValues 容许快速将属性添加到 ConfigurableEnvironmentConfigurableApplicationContext。可使用 key=value 字符串调用它,以下所示:

/** @author YiFeiXi */
@SpringBootTest
public class TestPropertyValueTests {
  @Autowired private ConfigurableEnvironment environment;

  @Test
  void test() {
    TestPropertyValues.of("org=spring", "name=boot").applyTo(environment);
    String org = environment.getProperty("org");
    String name = environment.getProperty("name");
    assertThat(org).isEqualTo("spring");
    assertThat(name).isEqualTo("boot");
  }
}

OutputCapture

OutputCapture 是一个JUnit扩展,可用于捕获 System.outSystem.err 输出。添加 @ExtendWith(OutputCaptureExtension.class) 并将 CapturedOutput 做为参数注入测试类构造函数或测试方法来使用,以下所示:

/**
 * 输出捕捉
 *
 * @author YiFeiXi
 */
@ExtendWith(OutputCaptureExtension.class)
public class OutputCaptureTests {

  @Test
  void test(CapturedOutput output) {
    System.out.print("hello world!");
    assertThat(output).isEqualTo("hello world!");
  }
}

TestRestTemplate

TestRestTemplate 是Spring的 RestTemplate 的一个方便的替代品,它在集成测试中很是有用。您能够获得一个普通模板或一个发送基本HTTP身份验证的模板(带有用户名和密码)。在这两种状况下,模板都以测试友好的方式运行,不会在服务器端错误上引起异常。

Spring Framework 5.0提供了一个新的WebTestClient,可用于WebFlux集成测试以及WebFlux和MVC端到端测试。它为断言提供了一个流畅的API,与 TestRestTemplate 不一样。
建议使用ApacheHTTP客户端(4.3.2或更高版本),但不是强制的。若是在类路径中有,TestRestTemplate 将经过适当配置 client 来响应。若是您确实使用了Apache的HTTP客户端,则会启用一些附加的测试友好功能:

  • 不遵循重定向(所以您能够断言响应位置)。
  • Cookies被忽略(所以模板是无状态的)。

TestRestTemplate 能够在集成测试中直接实例化,以下例所示:

/** @author YiFeiXi */
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class RestTemplateTests {

  @Test
  void test() {
    TestRestTemplate template = new TestRestTemplate();
    String responseBody = template.getForObject("http://127.0.0.1:8080/", String.class);
    assertThat(responseBody).isEqualTo("Hello World!");
  }
}

或者,若是将 @SpringBootTest 注解与 WebEnvironment.RANDOM_PORTWebEnvironment.DEFINED_PORT 一块儿使用,则能够插入彻底配置的 TestRestTemplate 并开始使用它。若是须要,能够经过 RestTemplateBuilder bean应用其余定制。任何未指定 host 和 port 的URL都会自动链接到嵌入式服务器,以下例所示:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SampleWebClientTests {

    @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 Config {

        @Bean
        RestTemplateBuilder restTemplateBuilder() {
            return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
                    .setReadTimeout(Duration.ofSeconds(1));
        }
    }
}

资料

官方文档
代码示例
公众号:逸飞兮(专一于 Java 领域知识的深刻学习,从源码到原理,系统有序的学习)

逸飞兮

相关文章
相关标签/搜索