Spring Framework 6.2.0-M1:在測試中覆蓋 Bean

工程 | Simon Baslé | 2024 年 4 月 16 日 | ...

Spring Framework 6.2.0-M1 已釋出,其中包含解決一百多個問題的更改。這些更改中包括 Spring 測試支援中的一系列新特性。

在這篇文章中,我想向大家介紹這些新測試特性中的一個:Bean 覆蓋支援。

現狀回顧

使用 Spring TestContext Framework,您可以透過註解驅動的模型輕鬆地在整合測試中驗證 Spring 應用程式的正確連線。

在單元測試中,依賴注入和 Spring 設計原則使您的程式碼對容器的依賴性降低,並且更容易手動樁或模擬元件的依賴項以便進行隔離測試。在整合測試中,這一點不太相關,因為測試旨在覆蓋元件的正確連線。儘管您可能會發現在整合測試中需要替換 bean 的情況。

Spring Framework 團隊通常不建議重新定義 bean。儘管在 `BeanDefinitionRegistry` 的預設實現中透過一個標誌目前可以實現,但我們計劃棄用它,並且 Spring Boot 已經透過預設關閉 bean 覆蓋來選擇退出。

然而,這更多是生產程式碼中的擔憂,我們承認在測試中覆蓋 bean 是有用且合理的。因此,我們旨在為該領域常見的覆蓋場景提供一流的安排。

在 Spring Framework `6.2.0-M1` 中,我們引入了一個可擴充套件的 bean 覆蓋特性,它允許您在整合測試中精確且明確地替換一個或多個 bean 定義,同時防止生產程式碼或測試的其他部分出現此類意外更改。

使用 @TestBean 進行簡單的基於方法的覆蓋

Spring TestContext Framework 現在提供了一個簡單的 Bean 覆蓋支援實現:`@TestBean` 註解。

覆蓋名為 `example` 的 bean 分三步完成:新增一個與 bean 同名的欄位,用 `@TestBean` 註解它,並新增一個名為 `exampleTestOverride` 的無引數 `static` 工廠方法。在該工廠方法中,如果 bean 型別是介面,您可以返回一個簡化的實現,如下例所示:

@Configuration
class ProdConfiguration {

  @Bean
  MyService customService() {
    return new ProdServiceImpl();
  }
}

@SpringJUnitConfig
class MyServiceIntegrationTests {

  @TestBean
  MyService customService;

  static MyService customServiceTestOverride() {
    return new SimplifiedServiceImpl();
  }

  @Test
  void test(ApplicationContext context) {
    assertThat(context.getBean("customService")
      .isSameAs(this.customService)
      .isInstanceOf(SimplifiedServiceImpl.class);
    //...
  }
}

除非為 `@TestBean` 註解提供了 `beanName` 屬性,否則帶註解欄位的名稱將被解釋為目標 bean 的名稱。

`methodName` 引數也可用於指向不遵循預設命名約定 `{beanName}TestOverride` 的工廠方法。

Bean 覆蓋機制負責解析此註解並替換登錄檔中的現有 bean 定義。測試類中的 `customService` 欄位也會注入由 `customServiceTestOverride` 工廠方法生成的覆蓋例項。

使用 @MockitoBean@MockitoSpyBean 進行基於 Mockito 的覆蓋

這第二個 bean 覆蓋實現基於 Mockito 庫。它帶有兩個註解:@MockitoBean 用於自動將目標單例 bean 替換為 mock,以及 @MockitoSpyBean 用於將 bean 包裝在 spy 中。

這些註解都包含 Mockito 特有的屬性,以便進一步配置目標 bean 如何進行 mocking。這包括支援指定 mock 在測試之間如何重置,如下例所示:

@Configuration
class ProdConfiguration {

  @Bean
  MyService customService() {
    return new ProdService();
  }
}

@SpringJUnitConfig
class MyServiceIntegrationTests {

  @MockitoSpyBean(reset = MockReset.NONE)
  MyService customService;

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

在上面的示例中,spy 在測試之間將不會重置。預設情況下,mock 和 spy 會在測試方法執行重置。

請注意,要對 bean 進行 spy,首先必須存在被 spy 類的一個實際例項。Bean 覆蓋特性支援這種特殊情況,允許在 bean 例項化後從元資料建立覆蓋,此外還支援更常見的替換 bean 定義的情況。

擴充套件您自己的實現

新的測試中的 Bean 覆蓋支援採用基於註解的模型,適用於您測試類中的欄位。它是可擴充套件和可定製的,上面介紹的 3 個註解只是我們提供的預設實現。

實現您自己的 Bean 覆蓋方式就像實現以下內容一樣簡單:

  • 一個用 `@BeanOverride` 元註解標記的註解,該註解定義了要使用的 `BeanOverrideProcessor`。
  • `BeanOverrideProcessor` 實現本身。
  • 由處理器提供的一個或多個具體的 `OverrideMetadata` 實現。

Spring TestContext Framework 解析測試類,查詢任何用 `@BeanOverride` 元註解標記的欄位,並例項化相關的 `BeanOverrideProcessor` 以註冊一個 `OverrideMetadata` 例項。

然後 `BeanFactoryPostProcessor` 將使用該資訊修改上下文,根據每個元資料註冊和替換 bean 定義。

結論

Spring TestContext Framework 現在提供了兩種在測試中覆蓋 bean 的方法,而沒有意外副作用的風險。bean 覆蓋機制是可擴充套件的,如果您更喜歡使用 Mockito 以外的 mocking 庫,這將非常方便。

我們期待社群對這項功能的反饋,包括對第一版改進的建議。

與此同時,程式設計愉快!

訂閱 Spring 電子報

訂閱 Spring 電子報,保持連線

訂閱

領先一步

VMware 提供培訓和認證,為您的發展注入動力。

瞭解更多

獲得支援

Tanzu Spring 透過一個簡單的訂閱,為 OpenJDK™、Spring 和 Apache Tomcat® 提供支援和二進位制檔案。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視全部