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 的 0 引數 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 的模擬方式。這包括支援指定模擬如何在測試之間重置,如下例所示

@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,首先必須存在一個被監視類的實際例項。Bean 覆蓋功能支援此特殊情況,並允許在例項化 bean 後從元資料建立覆蓋,此外還有更常見的替換 bean 定義的情況。

使用您自己的實現進行擴充套件

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

實現您自己的 Bean 覆蓋版本就像實現以下內容一樣簡單

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

Spring TestContext Framework 解析測試類,查詢任何帶有 @BeanOverride 元註解的欄位,並例項化相關的 BeanOverrideProcessor 以註冊 OverrideMetadata 例項。

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

結論

Spring TestContext Framework 現在提供了兩種在測試中覆蓋 bean 的方法,而沒有意外副作用的風險。bean 覆蓋機制是可擴充套件的,例如,如果您喜歡使用 Mockito 以外的模擬庫,這可能會派上用場。

我們期待社群對該功能的反饋,包括對首次迭代的改進建議。

同時,祝您編碼愉快!

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

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

檢視所有