SpringBootTest 測試工具


SpringBootTest 測試工具

以下內容,翻譯自官方文檔,並結合了學習過程的demo。


Spring Boot提供了許多實用程序和注解,幫助測試應用程序。測試支持由兩個模塊提供:spring-boot-test 包含核心項,spring-boot-test-autoconfigure 支持測試的自動配置。

大多數開發人員使用 spring-boot-starter-test,它同時導入 SpringBoot 測試模塊以及JUnit Jupiter、AssertJ、Hamcrest和許多其他有用的庫。

此文使用當前最新穩定版本: SpringBoot 2.2.2.RELEASE
此 starter 還帶來了 vintage  引擎,因此可以同時運行JUnit 4和JUnit 5測試。如果已經將測試遷移到JUnit5,那么應該排除JUnit4支持,如下例所示:

    <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)包含以下庫:

  • JUnit 5:包含兼容 JUnit 4,Java 應用程序單元測試的事實標准
  • Spring Test 和 SpringBootTest:對Spring Boot應用程序的公共和集成測試支持。
  • AssertJ:流式斷言庫
  • Hamcrest:匹配對象庫
  • Mockito:Java 模擬框架
  • JSONassert:JSON 斷言庫
  • JsonPath:JSON XPath

測試 Spring 應用

依賴注入的一個主要優點是它應該使代碼更容易進行單元測試。可以使用新的操作符實例化對象,甚至不涉及Spring。也可以使用模擬對象而不是實際依賴項。

通常,您需要超越單元測試,開始集成測試(使用Spring ApplicationContext)。能夠在不需要部署應用程序或連接到其他基礎結構的情況下執行集成測試是非常有用的。

Spring框架包含了一個專門的集成測試模塊。可以直接向 org.springframework:spring 測試聲明一個依賴項,或者使用 spring-boot-starter-test

如果以前沒有使用過 spring-test 模塊,那么應該從閱讀spring框架參考文檔的相關部分開始。

測試 SpringBoot 應用

SpringBoot 應用程序是 Spring ApplicationContext,因此除了使用普通的Spring上下文之外,不必做任何特別的事情來測試它。

默認情況下,只有在使用 SpringApplication 創建 Spring Boot時,它的外部屬性、日志記錄和其他特性才會安裝在上下文中。

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

逸飛兮


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM