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 領域知識的深刻學習,從源碼到原理,系統有序的學習)

逸飛兮

相關文章
相關標籤/搜索