Spring TestContext
框架(位於org.springframework.test.context
包中)提供了通用的、註解驅動的單元和集成測試支持,這些支持與所使用的測試框架無關。TestContext
框架還非常重視約定優於配置,你可以通過基於註解的配置覆蓋合理的默認值。
除了通用測試基礎結構之外,TestContext
框架還為JUnit 4,JUnit Jupiter(AKA JUnit 5)和TestNG提供了顯式支持。對於JUnit 4和TestNG,Spring提供了抽象支持類。此外,Spring為JUnit 4提供了自定義JUnit Runner和自定義JUnit規則,以及JUnit Jupiter的自定義擴展,可讓你編寫所謂的POJO測試類。不需要POJO測試類來擴展特定的類層次結構,例如抽象支持類。下一節概述了TestContext
框架的內部。如果你僅對使用框架感興趣,而對使用自己的自定義監聽器或自定義加載程序進行擴展不感興趣,請直接轉到配置(上下文管理 、依賴項注入、事務管理、支持類和註解支持部分。
3.5.1 關鍵抽象
該框架的核心由TestContextManager
類和TestContext
,TestExecutionListener
和SmartContextLoader
接口組成。為每個測試類創建一個TestContextManager
(例如,用於在JUnit Jupiter中的單個測試類中執行所有測試方法)。反過來,TestContextManager
管理包含當前測試上下文的TestContext
。隨著測試的進行,TestContextManager
還更新了TestContext
的狀態,並委託給TestExecutionListener
實現,該實現通過提供依賴項注入,管理事務等來檢測實際的測試執行。SmartContextLoader
負責為給定的測試類加載ApplicationContext
。有關更多信息和各種實現的示例,請參見javadoc和Spring測試套件。
TestContext
TestContext
封裝了在其中執行測試的上下文(與使用中的實際測試框架無關),併為其負責的測試實例提供了上下文管理和緩存支持。如果需要,TestContext
還委託給SmartContextLoader
來加載ApplicationContext
。
TestContextManager
TestContextManager
是Spring TestContext
框架的主要入口點,並負責管理單個TestContext
並在定義良好的測試執行點向每個註冊的TestExecutionListener
發出事件信號:
- 在任何
before
類之前或在特定測試框架的所有方法之前。 - 測試實例後處理。
- 在任何
before
或在每個特定測試框架的方法之前。 - 在執行測試方法之前但在測試設置之後。
- 在測試方法執行之後,但在測試拆卸之前。
- 之後的任何方法或之後的每一個特定的測試框架。
- 在特定測試框架的任何類後或所有方法之後。
TestExecutionListener
TestExecutionListener
定義用於對由註冊監聽器的TestContextManager
發佈的測試執行事件做出反應的API。請參閱TestExecutionListener配置。
上下文加載器
ContextLoader
是一個策略接口,用於為Spring TestContext
框架管理的集成測試加載ApplicationContext
。你應該實現SmartContextLoader
而不是此接口,以提供對組件類,激活的bean定義配置文件、測試屬性源、上下文層次結構和WebApplicationContext
支持的支持。
SmartContextLoader
是ContextLoader
接口的擴展,它取代了原始的最小ContextLoader
SPI。具體來說,SmartContextLoader
可以選擇處理資源位置、組件類或上下文初始化器。此外,SmartContextLoader
可以在其加載的上下文中設置激活Bean定義配置文件並測試屬性源。
Spring提供了以下實現:
-
DelegatingSmartContextLoader
: 它是兩個默認加載器之一,它在內部委派給AnnotationConfigContextLoader
、GenericXmlContextLoader
或GenericGroovyXmlContextLoader
,具體取決於為測試類聲明的配置或默認位置或默認配置類的存在。僅當Groovy在類路徑上時才啟用Groovy支持。 -
WebDelegatingSmartContextLoader
: 它是兩個默認加載器之一,它在內部委派給AnnotationConfigWebContextLoader
、GenericXmlWebContextLoader或GenericGroovyXmlWebContextLoader
,具體取決於為測試類聲明的配置或默認位置或默認配置類的存在。僅當測試類上存在@WebAppConfiguration
時,才使用WebContextLoader
。僅當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
,實現自定義的TestContext
或ContextCache
,擴展默認的ContextCustomizerFactory
和TestExecutionListener
實現等等。為了對TestContext
框架的運行方式進行低級別控制,Spring提供了引導策略。
TestContextBootstrapper
定義了用於引導TestContext
框架的SPI。TestContextManager
使用TestContextBootstrapper
加載當前測試的TestExecutionListener
實現並構建它管理的TestContext
。你可以直接使用@BootstrapWith
或作為元註解,為測試類(或測試類層次結構)配置自定義引導策略。如果沒有通過使用@BootstrapWith
顯式配置引導程序,則根據@WebAppConfiguration
的存在,使用DefaultTestContextBootstrapper
或WebTestContextBootstrapper
。
由於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
屬性文件中的key
為org.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
實現。
為避免必須瞭解並重新聲明所有默認監聽器,可以將@TestExecutionListeners
的mergeMode
屬性設置為MergeMode.MERGE_WITH_DEFAULTS
。MERGE_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
的其他功能。相反,TestExecutionListener
在ApplicationContext
中不是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/liyong1028826685
微信公眾號:
技術交流群: