Spring 5 中文解析核心篇-集成測試之TestContext(上)

Spring TestContext 框架(位於org.springframework.test.context包中)提供了通用的、註解驅動的單元和集成測試支持,這些支持與所使用的測試框架無關。TestContext框架還很是重視約定優於配置,你能夠經過基於註解的配置覆蓋合理的默認值。html

除了通用測試基礎結構以外,TestContext框架還爲JUnit 4,JUnit Jupiter(AKA JUnit 5)和TestNG提供了顯式支持。對於JUnit 4和TestNG,Spring提供了抽象支持類。此外,Spring爲JUnit 4提供了自定義JUnit Runner和自定義JUnit規則,以及JUnit Jupiter的自定義擴展,可以讓你編寫所謂的POJO測試類。不須要POJO測試類來擴展特定的類層次結構,例如抽象支持類。下一節概述了TestContext框架的內部。若是你僅對使用框架感興趣,而對使用本身的自定義監聽器或自定義加載程序進行擴展不感興趣,請直接轉到配置(上下文管理依賴項注入事務管理支持類註解支持部分。java

3.5.1 關鍵抽象

該框架的核心由TestContextManager類和TestContextTestExecutionListenerSmartContextLoader接口組成。爲每一個測試類建立一個TestContextManager(例如,用於在JUnit Jupiter中的單個測試類中執行全部測試方法)。反過來,TestContextManager管理包含當前測試上下文的TestContext。隨着測試的進行,TestContextManager還更新了TestContext的狀態,並委託給TestExecutionListener實現,該實現經過提供依賴項注入,管理事務等來檢測實際的測試執行。SmartContextLoader負責爲給定的測試類加載ApplicationContext。有關更多信息和各類實現的示例,請參見javadoc和Spring測試套件。git

TestContext算法

TestContext封裝了在其中執行測試的上下文(與使用中的實際測試框架無關),併爲其負責的測試實例提供了上下文管理和緩存支持。若是須要,TestContext還委託給SmartContextLoader來加載ApplicationContextspring

TestContextManager編程

TestContextManager是Spring TestContext 框架的主要入口點,並負責管理單個TestContext並在定義良好的測試執行點向每一個註冊的TestExecutionListener發出事件信號:api

  • 在任何before類以前或在特定測試框架的全部方法以前。
  • 測試實例後處理。
  • 在任何before或在每一個特定測試框架的方法以前。
  • 在執行測試方法以前但在測試設置以後。
  • 在測試方法執行以後,但在測試拆卸以前。
  • 以後的任何方法或以後的每個特定的測試框架。
  • 在特定測試框架的任何類後或全部方法以後。

TestExecutionListener緩存

TestExecutionListener定義用於對由註冊監聽器的TestContextManager發佈的測試執行事件作出反應的API。請參閱TestExecutionListener配置。微信

上下文加載器app

ContextLoader是一個策略接口,用於爲Spring TestContext 框架管理的集成測試加載ApplicationContext。你應該實現SmartContextLoader而不是此接口,以提供對組件類,激活的bean定義配置文件、測試屬性源、上下文層次結構和WebApplicationContext支持的支持。

SmartContextLoaderContextLoader接口的擴展,它取代了原始的最小ContextLoader SPI。具體來講,SmartContextLoader能夠選擇處理資源位置、組件類或上下文初始化器。此外,SmartContextLoader能夠在其加載的上下文中設置激活Bean定義配置文件並測試屬性源。

Spring提供瞭如下實現:

  • DelegatingSmartContextLoader: 它是兩個默認加載器之一,它在內部委派給AnnotationConfigContextLoaderGenericXmlContextLoaderGenericGroovyXmlContextLoader,具體取決於爲測試類聲明的配置或默認位置或默認配置類的存在。僅當Groovy在類路徑上時才啓用Groovy支持。
  • WebDelegatingSmartContextLoader: 它是兩個默認加載器之一,它在內部委派給AnnotationConfigWebContextLoader、GenericXmlWebContextLoader或GenericGroovyXmlWebContextLoader,具體取決於爲測試類聲明的配置或默認位置或默認配置類的存在。僅當測試類上存在@WebAppConfiguration時,才使用Web ContextLoader。僅當Groovy在類路徑上時才啓用Groovy支持。
  • AnnotationConfigContextLoader:從組件類加載標準ApplicationContext
  • AnnotationConfigWebContextLoader: 從組件類加載WebApplicationContext
  • GenericGroovyXmlContextLoader: 從Groovy腳本或XML配置文件的資源位置加載標準ApplicationContext
  • GenericGroovyXmlWebContextLoader: 從Groovy腳本或XML配置文件的資源位置加載WebApplicationContext
  • GenericXmlContextLoader: 從XML資源位置加載標準ApplicationContext
  • GenericXmlWebContextLoader: 從XML資源位置加載WebApplicationContext
  • GenericPropertiesContextLoader:從Java屬性文件加載標準ApplicationContext
3.5.2 引導TestContext框架

Spring TestContext 框架內部的默認配置足以知足全部常見用例。可是,有時開發團隊或第三方框架但願更改默認的ContextLoader,實現自定義的TestContextContextCache,擴展默認的ContextCustomizerFactoryTestExecutionListener實現等等。爲了對TestContext框架的運行方式進行低級別控制,Spring提供了引導策略。

TestContextBootstrapper定義了用於引導TestContext框架的SPI。TestContextManager使用TestContextBootstrapper加載當前測試的TestExecutionListener實現並構建它管理的TestContext。你能夠直接使用@BootstrapWith或做爲元註解,爲測試類(或測試類層次結構)配置自定義引導策略。若是沒有經過使用@BootstrapWith顯式配置引導程序,則根據@WebAppConfiguration的存在,使用DefaultTestContextBootstrapperWebTestContextBootstrapper

因爲TestContextBootstrapper SPI未來可能會更改(以適應新的需求),咱們強烈建議實現者不要直接實現此接口,而應擴展AbstractTestContextBootstrapper或其具體子類之一。

3.5.3 TestExecutionListener配置

Spring提供瞭如下TestExecutionListener實現,這些實現默認狀況下按如下順序註冊:

  • ServletTestExecutionListener:爲WebApplicationContext配置Servlet API模擬。
  • DirtiesContextBeforeModesTestExecutionListener:處理before模式的@DirtiesContext註解。
  • DependencyInjectionTestExecutionListener: 爲測試實例提供依賴項注入。
  • DirtiesContextTestExecutionListener: 處理after模式的@DirtiesContext註解。
  • TransactionalTestExecutionListener: 提供具備默認回滾語義的事務測試執行。
  • SqlScriptsTestExecutionListener: 運行使用@Sql註釋配置的SQL腳本。
  • EventPublishingTestExecutionListener: 將測試執行事件發佈到測試的ApplicationContext中(請參閱測試執行事件)。

註冊TestExecutionListener實現

你可使用@TestExecutionListeners註解爲測試類及其子類註解TestExecutionListener實現。有關詳細信息和示例,請參見註解支持@TestExecutionListeners的javadoc。

默認TestExecutionListener實現自動發現

經過使用@TestExecutionListeners註冊TestExecutionListener實現適用於有限測試方案中使用的自定義監聽器。可是,若是須要在整個測試套件中使用自定義監聽器,則會變得很麻煩。經過SpringFactoriesLoader機制支持自動發現默認的TestExecutionListener實現,能夠解決這個問題。

具體來講,spring-test模塊在其META-INF/spring.factories屬性文件中的keyorg.springframework.test.context.TestExecutionListener下聲明全部核心默認TestExecutionListener實現。第三方框架和開發人員能夠經過本身的META-INF/spring.factories屬性文件以相同的方式將本身的TestExecutionListener實現貢獻到默認監聽器列表中。

TestExecutionListener順序實現

TestContext框架經過上述SpringFactoriesLoader機制發現默認TestExecutionListener實現時,實例化的監聽器將使用Spring的AnnotationAwareOrderComparator進行排序,該類將使用Spring的Ordered接口和@Order註解進行排序。Spring提供的AbstractTestExecutionListener和全部默認的TestExecutionListener實現以適當的值實現Ordered。所以,第三方框架和開發人員應經過實施Ordered或聲明@Order來確保按默認順序註冊其默認的TestExecutionListener實現。請參閱javadoc以獲取核心默認TestExecutionListener實現的getOrder()方法,以獲取有關爲每一個核心監聽器分配哪些值的詳細信息。

TestExecutionListener合併實現

若是經過@TestExecutionListeners註冊了自定義TestExecutionListener,則不會註冊默認監聽器。在大多數常見的測試方案中,這有效地迫使開發人員手動聲明除任何自定義監聽器以外的全部默認監聽器。

下面的清單演示了這種配置樣式:

@ContextConfiguration
@TestExecutionListeners({
    MyCustomTestExecutionListener.class,
    ServletTestExecutionListener.class,
    DirtiesContextBeforeModesTestExecutionListener.class,
    DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    SqlScriptsTestExecutionListener.class
})
class MyTest {
    // class body...
}

這種方法的挑戰在於,它要求開發人員確切地知道默認狀況下注冊了哪些監聽器。此外,默認的監聽器集能夠隨版本的不一樣而變化-例如,在Spring框架4.1中引入了SqlScriptsTestExecutionListener,在Spring框架4.2中引入了DirtiesContextBeforeModesTestExecutionListener。此外,諸如Spring Boot和Spring Security之類的第三方框架經過使用上述自動發現機制註冊了本身的默認TestExecutionListener實現。

爲避免必須瞭解並從新聲明全部默認監聽器,能夠將@TestExecutionListenersmergeMode屬性設置爲MergeMode.MERGE_WITH_DEFAULTSMERGE_WITH_DEFAULTS表示應將本地聲明的監聽器與默認監聽器合併。合併算法可確保從列表中刪除重複項,並確保根據AnnotationAwareOrderComparator的語義對合並後的監聽器集進行排序,如Ordering TestExecutionListener實現中所述。若是監聽器實現Ordered或使用@Order進行註解,則它能夠影響將其與默認值合併的位置。不然,合併時,本地聲明的監聽器將追加到默認偵聽器列表中。

例如,若是上一個示例中的MyCustomTestExecutionListener類將順序值(例如500)配置爲小於ServletTestExecutionListener的順序(剛好是1000),則MyCustomTestExecutionListener能夠自動與默認列表合併。在ServletTestExecutionListener前面,而且前面的示例能夠替換爲如下示例:

@ContextConfiguration
@TestExecutionListeners(
    listeners = MyCustomTestExecutionListener.class,
    mergeMode = MERGE_WITH_DEFAULTS
)
class MyTest {
    // class body...
}
3.5.4 測試執行事件

Spring框架5.2中引入的EventPublishingTestExecutionListener提供了一種實現自定義TestExecutionListener的替代方法。測試的ApplicationContext中的組件能夠監聽EventPublishingTestExecutionListener發佈的如下事件,每一個事件都與TestExecutionListener API中的方法相對應。

  • BeforeTestClassEvent
  • PrepareTestInstanceEvent
  • BeforeTestMethodEvent
  • BeforeTestExecutionEvent
  • AfterTestExecutionEvent
  • AfterTestMethodEvent
  • AfterTestClassEvent
只有當 ApplicationContext已經加載時,纔會發佈這些事件。

這些事件可能因爲各類緣由被使用,例如重置模擬bean或跟蹤測試執行。使用測試執行事件而不是實現自定義TestExecutionListener的一個優點是,測試執行事件能夠由在測試ApplicationContext中註冊的任何Spring bean所使用,而且此類bean能夠直接受益於依賴項注入和ApplicationContext的其餘功能。相反,TestExecutionListenerApplicationContext中不是bean。

爲了監聽測試執行事件,Spring Bean能夠選擇實現org.springframework.context.ApplicationListener接口。或者,可使用@EventListener註解監聽器方法,並將監聽方法配置爲監聽上面列出的特定事件類型之一(請參閱基於註解的事件監聽器)。因爲這種方法的流行,Spring提供瞭如下專用的@EventListener註解,以簡化測試執行事件監聽器的註冊。這些註解駐留在org.springframework.test.context.event.annotation包中。

  • @BeforeTestClass
  • @PrepareTestInstance
  • @BeforeTestMethod
  • @BeforeTestExecution
  • @AfterTestExecution
  • @AfterTestMethod
  • @AfterTestClass
參考代碼: org.liyong.test.annotation.test.spring.TestExecutionEventTest

異常處理

默認狀況下,若是測試執行事件監聽器在使用事件時拋出異常,則該異常將傳播到使用中的基礎測試框架(例如JUnit或TestNG)。例如,若是使用BeforeTestMethodEvent致使異常,則相應的測試方法將因異常而失敗。相反,若是異步測試執行事件監聽器引起異常,則該異常不會傳播到基礎測試框架。有關異步異常處理的更多詳細信息,請查閱@EventListener類級javadoc。

異步監聽器

若是你但願特定的測試執行事件監聽器異步處理事件,你可使用Spring的常規@Async支持。有關更多詳細信息,請查閱@EventListener的類級javadoc。

參考代碼: org.liyong.test.annotation.test.spring.TestExecutionEventTest

做者

我的從事金融行業,就任過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就任於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公衆號和博客站點對知識體系進行分享。關注公衆號: 青年IT男 獲取最新技術文章推送!

博客地址: http://youngitman.tech

CSDN: https://blog.csdn.net/liyong1...

微信公衆號:

技術交流羣:

相關文章
相關標籤/搜索