20191127 Spring Boot官方文档学习(4.25)

4.25. Testing

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

大多数开发人员都使用spring-boot-starter-test 启动器,该程序同时导入Spring Boot测试模块以及JUnit Jupiter,AssertJ,Hamcrest和许多其他有用的库。

启动程序还带来了老式引擎,因此您可以运行JUnit 4和JUnit 5测试。如果已将测试迁移到JUnit 5,则应排除对JUnit 4的支持,如以下示例所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

4.25.1。测试范围依赖项

spring-boot-starter-test 启动器(test scope中)包含以下提供的库:

  • JUnit 5(包括用于与JUnit 4向后兼容的老式引擎:事实上的标准,用于Java应用程序的单元测试。
  • Spring Test和Spring Boot Test:对Spring Boot应用程序的实用工具和集成测试支持。
  • AssertJ:流畅的断言库。
  • Hamcrest:匹配对象库(也是约束或谓词)。
  • Mockito:一个Java 模拟框架。
  • JSONassert:JSON的断言库。
  • JsonPath:JSON的XPath。

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

4.25.2。测试Spring应用程序

依赖注入的主要优点之一是,它应该使您的代码更易于进行单元测试。您可以使用new运算符实例化对象,而无需使用Spring。您还可以使用模拟对象而不是实际的依赖项。

通常,您需要超越单元测试并开始集成测试(使用Spring ApplicationContext)。能够进行集成测试而无需部署应用程序或连接到其他基础结构是很有用的。

Spring框架包括用于此类集成测试的专用测试模块。您可以直接向其声明org.springframework:spring-test依赖项,也可以使用spring-boot-starter-test“启动器”将其引入。

如果您以前没有使用过spring-test模块,则应先阅读Spring Framework参考文档的相关部分

4.25.3。测试Spring Boot应用程序

Spring Boot应用程序是Spring ApplicationContext,因此除了对普通Spring上下文进行正常测试以外,无需执行任何其他特殊操作即可对其进行测试。

默认情况下,Spring Boot的外部属性,日志记录和其他功能仅在用于SpringApplication创建时才安装在上下文中。

Spring Boot提供了一个@SpringBootTest注释,当您需要Spring Boot功能时,它可以用作标准注释spring-test @ContextConfiguration的替代。注释通过SpringApplication创建在测试中使用过的ApplicationContext来起作用。除了@SpringBootTest,还提供许多其他注释,用于测试应用程序的更多特定部分。

如果使用的是JUnit 4,请不要忘记也将@RunWith(SpringRunner.class)添加到测试中,否则注释将被忽略。如果您使用的是JUnit 5,则无需添加等价项@RunWith(SpringExtension.class)@SpringBootTest和其他@…Test注释已经在其中进行了注释。

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

  • MOCK(默认):加载Web ApplicationContext并提供模拟Web环境。使用此注释时,不会启动嵌入式服务器。如果您的类路径中没有网络环境,则此模式将透明地退回到创建常规非网络ApplicationContext。它可以与Web应用程序结合使用@AutoConfigureMockMvc@AutoConfigureWebTestClient用于基于模拟的测试。
  • RANDOM_PORT:加载WebServerApplicationContext并提供真实的网络环境。嵌入式服务器将启动并在随机端口上侦听。
  • DEFINED_PORT:加载WebServerApplicationContext并提供真实的网络环境。嵌入式服务器将启动并在已定义的端口(来自application.properties)上或在的默认8080端口上进行侦听。
  • NONE:通过使用SpringApplication加载ApplicationContext,但不提供任何网络环境(模拟或其他方式)。

如果您的测试是@Transactional,则默认情况下它将在每个测试方法的末尾回滚事务。但是,由于将这种安排与一个RANDOM_PORT或多个DEFINED_PORT隐式提供真实的servlet环境一起使用,HTTP客户端和服务器在单独的线程中运行,因此在单独的事务中运行。在这种情况下,服务器上启动的任何事务都不会回滚。

如果您的应用程序对管理服务器使用其他端口,则webEnvironment = WebEnvironment.RANDOM_PORT@SpringBootTest也将在单独的随机端口上启动管理服务器。

检测Web应用程序类型

如果Spring MVC可用,则配置基于常规MVC的应用程序上下文。如果您只有Spring WebFlux,我们将检测到它并配置基于WebFlux的应用程序上下文。

如果两者都存在,则Spring MVC优先。如果要在这种情况下测试反应式Web应用程序,则必须设置spring.main.web-application-type属性:

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

检测测试配置

如果您熟悉Spring Test Framework,则可能习惯于使用@ContextConfiguration(classes=...)来指定要加载哪个Spring @Configuration。另外,您可能经常在测试中使用@Configuration嵌套类。

在测试Spring Boot应用程序时,通常不需要这样做。只要您没有明确定义,Spring Boot的 @*Test注释就会自动搜索您的主要配置。

搜索算法从包含测试的程序包开始工作,直到找到带有@SpringBootApplication@SpringBootConfiguration注释的类。只要您以合理的方式对代码进行结构化,通常就可以找到您的主要配置。

如果使用测试注释来测试应用程序的更特定的部分,则应避免在main方法的应用程序类上添加特定于特定区域的配置设置。

@SpringBootApplication的基础组件扫描配置定义排除了用于确保切片按预期工作的过滤器。如果在@SpringBootApplication注解类上使用显式指令@ComponentScan,请注意这些过滤器将被禁用。如果使用切片,则应再次定义它们。

如果要自定义主要配置,则可以使用嵌套@TestConfiguration类。与将使用嵌套类代替应用程序的主要配置的@Configuration嵌套类不同,除了应用程序的主要配置之外,还使用了@TestConfiguration嵌套类。

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

排除测试配置

如果您的应用程序使用组件扫描(例如,如果使用@SpringBootApplication或@ComponentScan),您可能会发现仅为特定测试创建的顶级配置类到处意外使用。

如我们先前所见,可以在测试的内部类上使用@TestConfiguration来自定义主要配置。当放置在顶级类上时,@TestConfiguration指示不应通过扫描来拾取src/test/java中的类。然后,可以在需要的位置显式导入该类,如以下示例所示:

@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {

    @Test
    void exampleTest() {
        ...
    }

}

如果您直接使用@ComponentScan(即不是通过@SpringBootApplication),则需要向其注册TypeExcludeFilter

使用应用程序参数

如果您的应用程序需要参数,则可以使用args属性将其注入@SpringBootTest

@SpringBootTest(args = "--app.test=one")
class ApplicationArgumentsExampleTests {

    @Test
    void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }

}

在模拟环境中进行测试

默认情况下,@SpringBootTest不启动服务器。如果您有要在此模拟环境下进行测试的Web点,则可以另外配置MockMvc,如以下示例所示:

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.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 MockMvcExampleTests {

    @Test
    void exampleTest(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
    }

}

如果您只想专注于Web层而不希望完全完成ApplicationContext,请考虑使用@WebMvcTest代替。

或者,您可以配置WebTestClient,如以下示例所示:

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;

@SpringBootTest
@AutoConfigureWebTestClient
class MockWebTestClientExampleTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello World");
    }

}

在模拟环境中进行测试通常比在完整的Servlet容器中运行更快。但是,由于模拟发生在Spring MVC层,因此无法使用MockMvc直接测试依赖于较低级别Servlet容器行为的代码。

例如,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 RandomPortWebTestClientExampleTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello World");
    }

}

此设置要求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 RandomPortTestRestTemplateExampleTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");
    }

}

自定义WebTestClient

要自定义WebTestClient Bean,请配置WebTestClientBuilderCustomizer Bean。被WebTestClient.Builder调用的任何这样的bean用于创建WebTestClient。

使用JMX

由于测试上下文框架缓存上下文,因此默认情况下禁用JMX以防止相同组件在同一域上注册。如果此类测试需要访问MBeanServer,请考虑将其标记为dirty

@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class SampleJmxTests {

    @Autowired
    private MBeanServer mBeanServer;

    @Test
    void exampleTest() {
        // ...
    }

}

模拟和间谍Beans

运行测试时,有时有必要在应用程序上下文中模拟某些组件。例如,您可能在开发过程中无法使用某些远程服务的facade。当您要模拟在实际环境中可能难以触发的故障时,模拟也很有用。

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

如果您的测试使用Spring Boot的测试注释之一(例如@SpringBootTest),则此功能会自动启用。要以其他方式使用此功能,必须显式添加侦听器,如以下示例所示:

@TestExecutionListeners(MockitoTestExecutionListener.class)

下面的示例用RemoteService模拟实现替换现有的bean:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

@SpringBootTest
class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

@MockBean不能用于模拟应用程序上下文刷新期间执行的bean的行为。到执行测试时,应用程序上下文刷新已完成,并且配置模拟行为为时已晚。我们建议在这种情况下使用一种方法来创建和配置模拟@Bean。

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

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

如果您使用@SpyBean通过@Cacheable方法监视一个bean,则必须使用-parameters编译应用程序。这样可以确保一旦侦察到bean,参数名称就可用于缓存基础结构。

自动配置的测试

Spring Boot的自动配置系统适用于应用程序,但有时对于测试来说可能有点过多。它通常仅有助于加载测试应用程序“切片”所需的配置部分。例如,您可能想要测试Spring MVC控制器是否正确映射了URL,并且您不想在这些测试中涉及数据库调用,或者您想要测试JPA实体,并且对那些JPA实体不感兴趣,并且你在测试运行时对网络层不感兴趣。

spring-boot-test-autoconfigure模块包括许多注释,可用于自动配置此类“切片”。它们中的每一个都以类似的方式工作,提供了一个@...Test注释加载ApplicationContext以及一个或多个@AutoConfigure...,可用于自定义自动配置设置的注释。

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

@...Test不支持在一个测试中 通过使用多个注释来包含多个“切片” 。如果需要多个“切片”,请选择其中一个@...Test注释,并手动添加其他“切片” 的@AutoConfigure...注释。

也可以将@AutoConfigure...注释与标准@SpringBootTest注释一起使用。如果您对“切片”应用程序不感兴趣,但需要一些自动配置的测试bean,则可以使用此组合。

Auto-configured JSON Tests

Auto-configured Spring MVC Tests

Auto-configured Spring WebFlux Tests

Auto-configured Data JPA Tests

Auto-configured JDBC Tests

Auto-configured Data JDBC Tests

Auto-configured jOOQ Tests

Auto-configured Data MongoDB Tests

Auto-configured Data Neo4j Tests

Auto-configured Data Redis Tests

Auto-configured Data LDAP Tests

Auto-configured REST Clients

Auto-configured Spring REST Docs Tests

Additional Auto-configuration and Slicing

User Configuration and Slicing

Using Spock to Test Spring Boot Applications

4.25.4. Test Utilities

跳过

posted @ 2019-11-27 17:33  流星<。)#)))≦  阅读(518)  评论(0编辑  收藏  举报