本節(本章其餘部分)涵蓋了Spring應用程序的集成測試。它包括以下主題:
3.1 概要
能夠執行一些集成測試而無需部署到應用程序服務器或連接到其他企業基礎結構,這一點很重要。這樣可以測試以下內容:
- 正確連接Spring IoC容器上下文。
- 使用JDBC或ORM工具進行數據訪問。這可以包括諸如SQL語句的正確性、Hibernate查詢、JPA實體映射之類的東西。
Spring框架為Spring測試模塊中的集成測試提供了一流的支持。實際的JAR文件的名稱可能包括髮行版,也可能採用長org.springframework.test
格式,具體取決於你從何處獲取(請參閱“依賴管理”部分中的說明)。該庫包含org.springframework.test
包,其中包含用於與Spring容器進行集成測試的有價值的類。此測試不依賴於應用程序服務器或其他部署環境。此類測試的運行速度比單元測試慢,但比依賴於部署到應用程序服務器的等效Selenium測試或遠程測試快。
單元和集成測試支持以註解驅動的Spring TestContext 框架的形式提供。TestContext
框架與實際使用的測試框架無關,該框架允許各種環境(包括JUnit,TestNG和其他環境)中對測試進行檢測。
3.2 集成測試目標
Spring的集成測試支持的主要目標如下:
- 在測試之間管理Spring IoC容器緩存。
- 提供測試fixture實例的依賴注入。
- 提供適合集成測試的事務管理。
- 提供特定於Spring的基類,以幫助開發人員編寫集成測試
接下來的幾節描述了每個目標,並提供了有關實現和配製詳細信息的鏈接。
Fixture意思:JUnit提供了編寫測試前準備、測試後清理的固定代碼,我們稱之為Fixture
3.2.1 上下文管理和緩存
Spring TestContext
框架提供了Spring ApplicationContext
實例和WebApplicationContext
實例的一致加載以及這些上下文的緩存。支持加載上下文的緩存很重要,因為啟動時間可能會成為一個問題-不是因為Spring本身的開銷,而是因為Spring容器實例化的對象需要時間才能實例化。例如,具有50到100個Hibernate
映射文件的項目可能需要10到20秒來加載映射文件,並且在每個測試fixture中運行每個測試之前要承擔該消耗,這會導致整體測試運行速度變慢,從而降低了開發人員的工作效率。
測試類通常聲明XML或Groovy配置元數據的資源位置數組(通常是在類路徑中)或用於配置應用程序的組件類的數組。這些位置或類與web.xml
或其他用於生產部署的配置文件中指定的位置或類相同或相似。
默認情況下,加載後,已配置的ApplicationContext
將重新用每個測試。因此,每個測試套件僅產生一次安裝成本,並且隨後的測試執行要快得多。在這種情況下,術語“測試套件”是指所有測試都在相同JVM中運行,例如,所有測試都從給定項目或模塊的Ant
、Maven
或Gradle
構建運行。在不太可能的情況下,測試破壞了應用程序上下文並需要重新加載(例如,通過修改bean定義或應用程序對象的狀態),可以將TestContext
框架配置為重新加載配置並重建應用程序上下文,然後再執行下一個測試。
請參見使用TestContext框架進行上下文管理和上下文緩存。
3.2.2 測試裝置的依賴注入
當TestContext
框架加載你的應用程序上下文時,可以選擇地用依賴注入來配置測試類的實例。這提供了一種方便的機制,可以通過在應用程序上下文中使用預配置的bean來設置測試fixture
。這裡一個強大的好處是,你可以跨各種測試場景重用應用程序上下文(例如,用於配置spring管理的對象圖、事務代理、數據源實例等),從而避免為單個測試用例重複複雜的測試fixture
設置。
例如,考慮一個場景,其中我們有一個類(HibernateTitleRepository
),該類為Title域實體實現數據訪問邏輯。我們要編寫集成測試來測試以下方面:
- Spring配置:基本上,與
HibernateTitleRepository
bean的配置有關的一切都正確並存在嗎? -
Hibernate
映射文件配置:是否已正確映射所有內容,並且是否有正確的延遲加載配置? -
HibernateTitleRepository
的邏輯:此類的配置實例是否按預期執行?
請參見使用TestContext框架進行測試fixture
的依賴注入。
3.2.3 事物管理
訪問真實數據庫的測試中的一個常見問題是它們對持久存儲狀態的影響。即使使用開發數據庫,對狀態的更改也可能會影響以後的測試。同樣,許多操作(例如插入或修改持久數據)無法在事物之外執行(或驗證)。
TestContext
框架解決了這個問題。默認情況下,框架為每個測試創建並回滾事務。你可以編寫可以假定存在事務的代碼。如果在測試中調用事務代理對象,則對象將根據其配置事務語義正確運行。此外,如果測試方法在測試管理的事務中運行時刪除了選定表的內容,則該事務將默認回滾,並且數據庫將返回到執行測試之前的狀態。通過使用在測試的應用程序上下文中定義的PlatformTransactionManager
bean,可以為測試提供事務支持。
如果你想要提交一個事務(不常見,但是當你想要一個特定的測試填充或修改數據庫時,偶爾會有用),你可以通過使用@Commit
註解告訴TestContext
框架使事務提交而不是回滾。
請參閱使用TestContext框架進行事務管理。
3.2.4 集成測試支持的類
Spring TestContext
框架提供了幾個抽象支持類,這些基礎測試類為測試框架提供了定義明確的鉤子,以方便的實例變量和方法,可用於訪問以下內容:
- ApplicationContext,用於執行顯式的bean查找或測試整個上下文的狀態。
- 一個JdbcTemplate,用於執行SQL語句來查詢數據庫。你可以使用此類查詢在執行與數據庫相關的應用程序代碼之前和之後確認數據庫狀態,並且Spring確保此類查詢在與應用程序代碼相同的事務範圍內運行。與ORM工具一起使用時,請確保避免誤報。
另外,你可能希望使用針對你的項目的實例變量和方法構建自己的自定義,應用程序範圍的超類。
請參閱TestContext框架的支持類。
3.3 JDBC測試支持
org.springframework.test.jdbc
包包含JdbcTestUtils
,它是JDBC相關實用程序功能的集合,旨在簡化標準數據庫測試方案。具體來說,JdbcTestUtils
提供以下靜態實用程序方法。
-
countRowsInTable(..)
:計算給定表中的行數。 -
countRowsInTableWhere(..)
:使用提供的WHERE
子句計算給定表中的行數。 -
deleteFromTables(..)
:刪除指定表中的所有行。 -
deleteFromTableWhere(..)
: 使用提供的WHERE
子句從給定表中刪除行。
AbstractTransactionalJUnit4SpringContextTests
和AbstractTransactionalTestNGSpringContextTests
提供了便利的方法,這些方法委託給JdbcTestUtils
中的上述方法。spring-jdbc
模塊提供了對配置和啟動嵌入式數據庫的支持你可以在與數據庫交互的集成測試中使用它。有關詳細信息,請參見嵌入式數據庫支持和使用嵌入式數據庫測試數據訪問邏輯。
3.4 註解
本節介紹了在測試Spring應用程序時可以使用的註解。它包括以下主題:
3.4.1 Spring測試註解
Spring框架提供了以下特定於Spring的註解集,你可以在單元測試和集成測試中將它們與TestContext
框架結合使用。有關更多信息,請參見相應的javadoc,包括默認屬性值、屬性別名和其他詳細信息。
Spring的測試註解包括以下內容:
@BootstrapWith
@ContextConfiguration
@WebAppConfiguration
@ContextHierarchy
@ActiveProfiles
@TestPropertySource
@DynamicPropertySource
@DirtiesContext
@TestExecutionListeners
@Commit
@Rollback
@BeforeTransaction
@AfterTransaction
@Sql
@SqlConfig
@SqlMergeMode
@SqlGroup
@BootstrapWith
@BootstrapWith
是一個類級別的註解,可用於配置如何引導Spring TestContext
框架。具體來說,你可以使用@BootstrapWith
指定自定義TestContextBootstrapper
。有關更多詳細信息,請參見有關引導TestContext框架的部分。
參考代碼:
org.liyong.test.annotation.test.spring.ConfigClassApplicationContextTests
@ContextConfiguration
@ContextConfiguration
定義了用於確定如何為集成測試加載和配置ApplicationContext
的類級元數據。具體來說,@ContextConfiguration
聲明應用程序上下文資源位置或用於加載上下文的組件類。
資源位置通常是位於類路徑中的XML配置文件或Groovy腳本,而組件類通常是@Configuration
類。但是,資源位置也可以引用文件系統中的文件和腳本,組件類可以是@Component
類、@Service
類等等。有關更多詳細信息,請參見組件類。
以下示例顯示了一個指向XML文件的@ContextConfiguration
註解:
@ContextConfiguration("/test-config.xml") //
class XmlApplicationContextTests {
// class body...
}
- 引用XML文件。
以下示例顯示了一個@ContextConfiguration
註解,該註解引用了一個類:
@ContextConfiguration(classes = TestConfig.class) //1
class ConfigClassApplicationContextTests {
// class body...
}
- 引用類文件
參考代碼:
org.liyong.test.annotation.test.spring.ConfigClassApplicationContextTests
作為聲明資源位置或組件類的替代方法或補充,可以使用@ContextConfiguration
聲明ApplicationContextInitializer
類。以下示例顯示了這種情況:
@ContextConfiguration(initializers = CustomContextIntializer.class)
class ContextInitializerTests {
// class body...
}
參考代碼:
org.liyong.test.annotation.test.spring.ContextInitializerTests
你可以選擇使用@ContextConfiguration
來聲明ContextLoader
策略。但是,你通常不需要顯式配置加載器,因為默認加載器支持初始化程序以及資源位置或組件類。
以下示例同時使用配置位置和加載器:
@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class) //1
class CustomLoaderXmlApplicationContextTests {
// class body...
}
- 配置位置和自定義加載器。
@ContextConfiguration
為繼承資源位置或配置類以及超類聲明上下文初始化器提供支持。
有關更多詳細信息,請參見上下文管理和@ContextConfiguration
javadocs。
參考代碼:
org.liyong.test.annotation.test.spring.CustomLoaderXmlApplicationContextTests
@WebAppConfiguration
@WebAppConfiguration
是一個類級別的註解,可用於聲明為集成測試加載的ApplicationContext
應該是WebApplicationContext
。@WebAppConfiguration
僅存在於測試類上,可以確保為測試加@WebApplicationContext
,並使用默認值file:src/main/webapp
作為Web應用程序根目錄(也就是即資源基本路徑)。資源基礎路徑用於在後臺創建MockServletContext
,該MockServletContext
用作測試的WebApplicationContext
的ServletContext
。
以下示例顯示瞭如何使用@WebAppConfiguration
註解:
@ContextConfiguration
@WebAppConfiguration //
class WebAppTests {
// class body...
}
要覆蓋默認值,可以使用隱式值屬性指定其他基礎資源路徑。classpath:
和file:
資源前綴均受支持。如果未提供資源前綴,則假定該路徑是文件系統資源。以下示例顯示如何指定類路徑資源:
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources") //1
class WebAppTests {
// class body...
}
- 指定類路徑資源。
注意,@WebAppConfiguration
必須與@ContextConfiguration
一起使用,可以在單個測試類中使用,也可以在測試類層次結構中使用。
有關更多詳細信息,請參見@WebAppConfiguration javadoc。
參考代碼:
org.liyong.test.annotation.test.spring.WebAppTests
@ContextHierarchy
@ContextHierarchy
是一個類級註解,用於定義集成測試的ApplicationContext
實例的層次結構。 @ContextHierarchy
應該用一個或多個@ContextConfiguration
實例的列表聲明,每個實例定義上下文層次結構中的一個級別。以下示例演示了在單個測試類中使用@ContextHierarchy
(也可以在測試類層次結構中使用@ContextHierarchy
):
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
class ContextHierarchyTests {
// class body...
}
參考代碼:
org.liyong.test.annotation.test.spring.ContextHierarchyTests
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = AppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class WebIntegrationTests {
// class body...
}
參考代碼:
org.liyong.test.annotation.test.spring.WebIntegrationTests
如果需要合併或覆蓋測試類層次結構中上下文層次結構的給定級別的配置,則必須通過在類層次結構的每個對應級別上為@ContextConfiguration
中的name屬性提供相同的值來顯式地命名該級別。有關更多示例,請參見上下文層次結構和@ContextHierarchy
javadoc。
@ActiveProfiles
@ActiveProfiles
是一個類級別的註解,用於聲明在為集成測時加載ApplicationContext
時應啟用哪些bean定義配置文件。
以下示例表明dev
配置文件應處於活動狀態:
@ContextConfiguration
@ActiveProfiles("dev") //1
class DeveloperTests {
// class body...
}
- 指示開發配置文件應處於活動狀態。
以下示例表明dev
和integration
配置文件均應處於活動狀態:
@ContextConfiguration
@ActiveProfiles({"dev", "integration"}) //1
class DeveloperIntegrationTests {
// class body...
}
- 指示
dev
和integration
配置文件應該處於活動狀態。
@ActiveProfiles
提供了對繼承默認情況下超類聲明的活動bean定義配置文件的支持。你還可以通過實現自定義ActiveProfilesResolver
並使用@ActiveProfiles
的resolver
屬性對其進行註冊,以編程方式解析活動bean定義配置文件。
參見環境配置文件和@ActiveProfiles javadoc的上下文配置以獲得示例和更多細節。
參考代碼:
org.liyong.test.annotation.test.spring.DeveloperIntegrationTests
@TestPropertySource
@TestPropertySource
是類級別的註解,可用於配置屬性文件和內聯屬性的位置,這些屬性和內聯屬性將被添加到環境中針對集成測試加載的ApplicationContext
的PropertySources
集中。
下面的示例演示如何從類路徑聲明屬性文件:
@ContextConfiguration
@TestPropertySource("/test.properties") //1
class MyIntegrationTests {
// class body...
}
- 從類路徑根目錄中的test.properties獲取屬性。
下面的示例演示如何聲明內聯屬性:
@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" }) //1
class MyIntegrationTests {
// class body...
}
- 聲明時區和端口屬性。
有關示例和更多詳細信息,請參見使用測試屬性源進行上下文配置。
@DynamicPropertySource
@DynamicPropertySource
是方法級別的註解,可用於註冊動態屬性,以將動態屬性添加到環境中針對集成測試加載的ApplicationContext
的PropertySources
集中。當你不預先知道屬性的值時,例如,如果屬性是由外部資源管理的,例如由Testcontainers
項目管理的容器,則動態屬性很有用。
下面的示例演示如何註冊動態屬性:
@ContextConfiguration
class MyIntegrationTests {
static MyExternalServer server = // ...
@DynamicPropertySource //1
static void dynamicProperties(DynamicPropertyRegistry registry) { //2
registry.add("server.port", server::getPort); //3
}
// tests ...
}
- 使用
@DynamicPropertySource
註解靜態方法。 - 接受
DynamicPropertyRegistry
作為參數。 - 註冊要從服務器延遲檢索的動態
server.port
屬性。
有關更多詳細信息,請參見使用動態屬性源進行上下文配置。
@DirtiesContext
@DirtiesContext
表示底層的Spring ApplicationContext
在執行測試期間已被清理(即,該測試以某種方式修改或破壞了它(例如,通過更改單例bean的狀態)),應將其關閉。當應用程序上下文被標記為清理時,它將從測試框架的緩存中刪除並關閉。因此,對於需要具有相同配置元數據的上下文的後續測試,將重新構建底層Spring容器。
你可以將@DirtiesContext
用作同一類或類層次結構中的類級別和方法級別的註解。在這種情況下,取決於配置的methodMode
和classMode
,在任何此類帶註解的方法之前或之後以及當前測試類之前或之後,ApplicationContext
均標記為清理。
以下示例說明了在各種配置情況下何時清理上下文:
-
在當前測試類之前,在類模式設置為
BEFORE_CLASS
的類上聲明時。@DirtiesContext(classMode = BEFORE_CLASS) //1 class FreshContextTests { // some tests that require a new Spring container }
- 在當前測試類之前清理上下文。
-
在當前測試類之後,當在類模式設置為
AFTER_CLASS
(即默認類模式)的類上聲明時。@DirtiesContext class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
- 當前測試類之後清理上下文。
-
在當前測試類中的每個測試方法之後,在類模式設置為
AFTER_EACH_TEST_METHOD
的類上聲明時。@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
- 每種測試方法後清理上下文。
-
在當前測試之前,當在方法模式設置為
BEFORE_METHOD
的方法上聲明時。@DirtiesContext(methodMode = BEFORE_METHOD) //1 @Test void testProcessWhichRequiresFreshAppCtx() { // some logic that requires a new Spring container }
- 在當前測試方法之前清理上下文。
-
當前測試之後,當在方法模式設置為
AFTER_METHOD
的方法上聲明時(即默認方法模式)。@DirtiesContext //1 @Test void testProcessWhichDirtiesAppCtx() { // some logic that results in the Spring container being dirtied }
- 當前測試方法後清理上下文。
如果在使用@ContextHierarchy
將上下文配置為上下文層次結構的一部分的測試中使用@DirtiesContext
,則可以使用hierarchyMode
標誌控制清除上下文緩存的方式。默認情況下,使用窮舉算法清除上下文緩存,不僅包括當前級別,還包括共享當前測試共有的祖先上下文的所有其他上下文層次結構。駐留在公共祖先上下文的子層次結構中的所有ApplicationContext
實例都將從上下文緩存中刪除並關閉。如果窮舉算法對於特定用例來說過於強大,那麼你可以指定更簡單的當前級別算法,如下面的示例所示。
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
class BaseTests {
// class body...
}
class ExtendedTests extends BaseTests {
@Test
@DirtiesContext(hierarchyMode = CURRENT_LEVEL) //1
void test() {
// some logic that results in the child context being dirtied
}
}
- 使用當前級別的算法。
有關EXHAUSTIVE
和CURRENT_LEVEL
算法的更多詳細信息,請參見DirtiesContext.HierarchyMode javadoc。
@TestExecutionListeners
@TestExecutionListeners
定義了用於配置應在TestContextManager
中註冊的TestExecutionListener
實現的類級元數據。通常,@TestExecutionListeners
與@ContextConfiguration
結合使用。
下面的示例演示如何註冊兩個TestExecutionListener
實現:
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) //1
class CustomTestExecutionListenerTests {
// class body...
}
- 註冊兩個
TestExecutionListener
實現。
默認情況下,@TestExecutionListeners
支持繼承的監聽器。有關示例和更多詳細信息,請參見javadoc。
@Commit
@Commit
表示應在測試方法完成後提交用於事務性測試方法的事務。你可以將@Commit
用作@Rollback(false)
的直接替代品,以更明確地傳達代碼的意圖。與@Rollback
類似,@ Commit
也可以聲明為類級別或方法級別的註解。
以下示例顯示瞭如何使用@Commit
註解:
@Commit //1
@Test
void testProcessWithoutRollback() {
// ...
}
- 將測試結果提交到數據庫。
@Rollback
@Rollback
表示在測試方法完成後是否應回退用於事務性測試方法的事務。如果為true,則回滾該事務。否則,將提交事務(另請參見@Commit)。即使未明確聲明@Rollback
,Spring TestContext
框架中用於集成測試的回滾默認為true。
當聲明為類級註解時,@Rollback
定義測試類層次結構中所有測試方法的默認回滾語義。當聲明為方法級別的註解時,@Rollback
定義特定測試方法的回滾語義,從而可能覆蓋類級別的@Rollback
或@Commit
語義。
以下示例使測試方法的結果不回滾(即,結果已提交到數據庫):
@Rollback(false) //1
@Test
void testProcessWithoutRollback() {
// ...
}
- 不要回滾結果。
@BeforeTransaction
@BeforeTransaction
表示,對於已配置為使用Spring的@Transactional
註解在事務內運行的測試方法,帶註解的void方法應在事務開始之前運行。@BeforeTransaction
方法不需要public訪問限定,可以在基於Java 8的接口默認方法。
以下示例顯示瞭如何使用@BeforeTransaction
註解:
@BeforeTransaction //1
void beforeTransaction() {
// logic to be executed before a transaction is started
}
- 在事務之前運行此方法。
@AfterTransaction
@AfterTransaction
表示,對於已配置為通過使用Spring的@Transactional
註解在事務內運行的測試方法,帶註解的void方法應在事務結束後運行。@AfterTransaction
方法不需要public訪問限定,可以在基於Java 8的接口默認方法中聲明。
@AfterTransaction //1
void afterTransaction() {
// logic to be executed after a transaction has ended
}
- 事務後運行此方法。
@Sql
@Sql
用於註解測試類或測試方法,以配置在集成測試期間針對給定數據庫運行的SQL腳本。以下示例顯示瞭如何使用它:
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})//1
void userTest() {
// execute code that relies on the test schema and test data
}
- 運行此測試的兩個腳本。
有關更多詳細信息,請參見使用@Sql聲明式執行SQL腳本。
@SqlConfig
@SqlConfig
定義元數據,該元數據用於確定如何解析和運行使用@Sql
註解配置的SQL腳本。以下示例顯示瞭如何使用它:
@Test
@Sql(
scripts = "/test-user-data.sql",
config = @SqlConfig(commentPrefix = "`", separator = "@@") //
)
void userTest() {
// execute code that relies on the test data
}
- 在SQL腳本中設置註釋前綴和分隔符。
@SqlMergeMode
@SqlMergeMode
用於註釋測試類或測試方法,以配置是否將方法級@Sql
聲明與類級@Sql
聲明合併。如果未在測試類或測試方法上聲明@SqlMergeMode
,則默認情況下將使用OVERRIDE
合併模式。在OVERRIDE
模式下,方法級別的@Sql
聲明將有效地覆蓋類級別的@Sql
聲明。
請注意,方法級別的@SqlMergeMode
聲明將覆蓋類級別的聲明。
下面的示例演示如何在類級別使用@SqlMergeMode
。
@SpringJUnitConfig(TestConfig.class)
@Sql("/test-schema.sql")
@SqlMergeMode(MERGE) //1
class UserTests {
@Test
@Sql("/user-test-data-001.sql")
void standardUserProfile() {
// execute code that relies on test data set 001
}
}
- 對於類中的所有測試方法,將
@Sql
合併模式設置為MERGE
。
下面的示例演示如何在方法級別使用@SqlMergeMode
。
@SpringJUnitConfig(TestConfig.class)
@Sql("/test-schema.sql")
class UserTests {
@Test
@Sql("/user-test-data-001.sql")
@SqlMergeMode(MERGE) //1
void standardUserProfile() {
// execute code that relies on test data set 001
}
}
對於特定的測試方法,將@Sql
合併模式設置為MERGE
。
@SqlGroup
@SqlGroup
是一個容器註解,它聚合了多個@Sql
註解。你可以本地使用@SqlGroup
聲明多個嵌套的@Sql
註解,也可以將其與Java 8對可重複註解的支持結合使用,其中@Sql
可以在同一類或方法上多次聲明,從而隱式生成此容器註解。下面的示例顯示如何聲明一個SQL組:
@Test
@SqlGroup({ //1
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
void userTest() {
// execute code that uses the test schema and test data
}
- 聲明一組SQL腳本。
3.4.2 標準註解支持
Spring TestContext
框架的所有配置的標準語義都支持以下註解。請注意,這些註解並非特定於測試,可以在Spring 框架中的任何地方使用。
@Autowired
@Qualifier
@Value
-
@Resource
(javax.annotation
) 如果支持JSR-250 -
@ManagedBean
(javax.annotation
) 如果支持 JSR-250 -
@Inject
(javax.inject
) 如果支持 JSR-330 -
@Named
(javax.inject
) 如果支持 JSR-330 -
@PersistenceContext
(javax.persistence
) 如果支持JPA -
@PersistenceUnit
(javax.persistence
) 如果支持JPA @Required
-
@Transactional
(org.springframework.transaction.annotation
) with limited attribute support
JSR-250生命週期註解
在Spring
TestContext
框架中,可以在ApplicationContext
中配置的任何應用程序組件上使用具有標準語義的@PostConstruct
和@PreDestroy
。但是,這些生命週期註解在實際測試類中的使用受到限制。如果測試類中的方法使用
@PostConstruct
進行註解,則該方法將在基礎測試框架的before
方法之前運行(例如,使用JUnit Jupiter的@BeforeEach
註解的方法),並且該方法適用於測試類中的每個測試方法。另一方面,如果測試類中的方法使用@PreDestroy
註解,則該方法將永遠不會運行。因此,在測試類中,建議你使用來自基礎測試框架的測試生命週期回調,而不是@PostConstruct
和@PreDestroy
。
3.4.3 Spring JUnit4測試註解
以下註解僅在與SpringRunner、Spring的JUnit 4規則或Spring的JUnit 4支持類一起使用時才受支持:
@IfProfileValue
@IfProfileValue
表示已為特定測試環境啟用帶註解的測試。如果配置的ProfileValueSource
返回提供的名稱的匹配值,則使用測試。否則,測試將被禁用,並且實際上將被忽略。
你可以在類級別、方法級別或兩者上應用@IfProfileValue
。對於該類或其子類中的任何方法,@IfProfileValue
的類級別用法優先於方法級別用法。具體來說,如果在類級別和方法級別都啟用了測試,則啟用該測試。缺少@IfProfileValue
意味著隱式啟用了測試。這類似於JUnit 4的@Ignore
註解的語義,不同之處在於@Ignore
的存在始終會禁用測試。
以下示例顯示了具有@IfProfileValue
註解的測試:
@IfProfileValue(name="java.vendor", value="Oracle Corporation") //1
@Test
public void testProcessWhichRunsOnlyOnOracleJvm() {
// some logic that should run only on Java VMs from Oracle Corporation
}
- 僅當Java供應商是“ Oracle Corporation”時才運行此測試。
另外,你可以為@IfProfileValue
配置值列表(具有OR
語義)以在JUnit 4環境中實現類似於TestNG的測試組支持。考慮以下示例:
@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"}) //1
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
// some logic that should run only for unit and integration test groups
}
- 對單元測試和集成測試運行此測試。
@ProfileValueSourceConfiguration
@ProfileValueSourceConfiguration
是一個類級別的註解,它指定檢索通過@IfProfileValue
註解配置的配置文件值時要使用哪種ProfileValueSource
類型。如果未為測試聲明@ProfileValueSourceConfiguration
,則默認使用SystemProfileValueSource
。以下示例顯示瞭如何使用@ProfileValueSourceConfiguration
:
@ProfileValueSourceConfiguration(CustomProfileValueSource.class) //1
public class CustomProfileValueSourceTests {
// class body...
}
- 使用自定義配置文件值源。
參考代碼:
org.liyong.test.annotation.test.spring.ProfileValueTest
@Timed
@Timed
表示帶註解的測試方法必須在指定的時間段(以毫秒為單位)內完成執行。如果單元測試片段執行時間超過指定的時間段,則測試將失敗。
該時間段包括運行測試方法本身,測試的任何重複(請參見@Repeat
)以及測試套件的任何設置或拆除。以下示例顯示瞭如何使用它:
@Timed(millis = 1000)//1
public void testProcessWithOneSecondTimeout() {
// some logic that should not take longer than 1 second to execute
}
- 將測試時間設置為一秒。
Spring的@Timed
註解與JUnit 4的@Test(timeout = ...)
支持具有不同的語義。具體來說,由於JUnit 4處理測試執行超時的方式(即通過在單獨的線程中執行測試方法),如果測試花費的時間太長,@Test(timeout = ...)
會搶先通過測試。另一方面,Spring的@Timed
不會搶先通過測試,而是在失敗之前等待測試完成。
@Repeat
@Repeat
表示必須重複運行帶註解的測試方法。註解中指定了要執行測試方法的次數。重複執行的範圍包括測試方法本身的執行以及測試套件中任何安裝或拆除。以下示例顯示瞭如何使用@Repeat
註解:
@Repeat(10) //1
@Test
public void testProcessRepeatedly() {
// ...
}
- 重複此測試十次。
3.4.4 Spring JUnit Jupiter測試註解
以下註解僅在與SpringExtension
和JUnit Jupiter
(即JUnit 5中的編程模型)結合使用時才受支持:
@SpringJUnitConfig
@SpringJUnitConfig
是一個組合註解,它將JUnit Jupiter
中的@ExtendWith(SpringExtension.class)
與Spring TestContext
框架中的@ContextConfiguration
組合在一起。它可以在類級別用作@ContextConfiguration
的直接替代。關於配置選項,@ContextConfiguration
和@SpringJUnitConfig
之間的唯一區別是可以使用@SpringJUnitConfig
中的value屬性聲明組件類。
以下示例顯示如何使用@SpringJUnitConfig
註解指定配置類:
@SpringJUnitConfig(TestConfig.class) //1
class ConfigurationClassJUnitJupiterSpringTests {
// class body...
}
- 指定配置類。
以下示例顯示如何使用@SpringJUnitConfig
註解指定配置文件的位置:
@SpringJUnitConfig(locations = "/test-config.xml") //1
class XmlJUnitJupiterSpringTests {
// class body...
}
- 指定配置文件的位置。
有關更多詳細信息,請參見上下文管理以及@SpringJUnitConfig
和@ContextConfiguration
的javadoc。
@SpringJUnitWebConfig
@SpringJUnitWebConfig
是一個組合的註解,它將來自JUnit Jupiter
的@ExtendWith(SpringExtension.class)
與來自Spring TestContext
框架的@ContextConfiguration
和@WebAppConfiguration
組合在一起。你可以在類級別使用它作為@ContextConfiguration
和@WebAppConfiguration
的直接替代。關於配置選項,@ ContextConfiguration
和@SpringJUnitWebConfig
之間的唯一區別是可以使用@SpringJUnitWebConfig
中的value屬性來聲明組件類。另外,只能使用@SpringJUnitWebConfig
中的resourcePath
屬性來覆蓋@WebAppConfiguration
中的value
屬性。
以下示例顯示如何使用@SpringJUnitWebConfig
註解指定配置類:
@SpringJUnitWebConfig(TestConfig.class) //1
class ConfigurationClassJUnitJupiterSpringWebTests {
// class body...
}
- 指定配置類。
以下示例顯示如何使用@SpringJUnitWebConfig註解指定配置文件的位置:
@SpringJUnitWebConfig(locations = "/test-config.xml") //1
class XmlJUnitJupiterSpringWebTests {
// class body...
}
- 指定配置文件的位置。
有關更多詳細信息,請參見上下文管理以及@SpringJUnitWebConfig,@ContextConfiguration和@WebAppConfiguration的javadoc。
參考代碼:
org.liyong.test.annotation.test.spring.ConfigurationClassJUnitJupiterSpringWebTests
@TestConstructor
@TestConstructor
是類型級別的註解,用於配置如何從測試的ApplicationContext
中的組件自動連接測試類構造函數的參數。
如果在測試類上不存在@TestConstructor
或meta-present
,則將使用默認的測試構造函數自動裝配模式。有關如何更改默認模式的詳細信息,請參見下面的提示。但是請注意,構造函數上的@Autowired
本地聲明優先於@TestConstructor
和默認模式。
更改默認的測試構造函數自動裝配模式
可以通過將JVM系統屬性
spring.test.constructor.autowire.mode
設置為all來更改默認的測試構造函數自動裝配模式。或者,可以通過SpringProperties
機制更改默認模式。如果未設置
spring.test.constructor.autowire.mode
屬性,則不會自動裝配測試類構造函數。從Spring框架5.2開始,僅將
@TestConstructor
與SpringExtension
結合使用以與JUnit Jupiter一起使用。請注意,SpringExtension
通常會自動為你註冊-例如,在使用@SpringJUnitConfig
和@SpringJUnitWebConfig
之類的註解或Spring Boot Test中與測試相關的各種註解時。
@EnabledIf
@EnabledIf
用於表示已註解的JUnit Jupiter測試類或測試方法啟用,如果提供的表達式的值為true
,則應運行@EnabledIf
。具體來說,如果表達式的計算結果為Boolean.TRUE
或等於true
的字符串(忽略大小寫),則啟用測試。在類級別應用時,默認情況下也會自動啟用該類中的所有測試方法。
表達式可以是以下任意一種:
- Spring表達式語言。例如:
@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
- Spring
Environment
中可用屬性的佔位符。例如:@EnabledIf("${smoke.tests.enabled}")
- 文本文字。例如:
@EnabledIf("true")
但是請注意,不是屬性佔位符的動態解析結果的文本文字的實際值為零,因為@EnabledIf(“ false”)
等效於@Disabled
,而@EnabledIf(“ true”)
在邏輯上是沒有意義的。
你可以使用@EnabledIf
作為元註解來創建自定義的組合註釋。例如,你可以創建一個自定義@EnabledOnMac
註解,如下所示:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@EnabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Enabled on Mac OS"
)
public @interface EnabledOnMac {}
@DisabledIf
@DisabledIf
用於表示已註解的JUnit Jupiter測試類或測試方法被禁用,並且如果提供的表達式的值為true
,則不應執行該操作。具體來說,如果表達式的計算結果為Boolean.TRUE
或等於true的字符串(忽略大小寫),則測試將被禁用。當在類級別應用時,該類中的所有測試方法也會自動禁止。
表達式可以是以下任意一種:
- Spring表達式語言。例如:
@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
- Spring Environment中可用屬性的佔位符。例如:
@DisabledIf("${smoke.tests.disabled}")
- 文本文字:例如:
@DisabledIf("true")
但是請注意,不是屬性佔位符的動態解析結果的文本文字的實際值為零,因為@DisabledIf(“ true”
)等效於@Disabled
,而@DisabledIf(“ false”)
在邏輯上是沒有意義的。
你可以將@DisabledIf
用作元註釋,以創建自定義的組合註解。例如,你可以創建一個自定義@DisabledOnMac
註解,如下所示:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Disabled on Mac OS"
)
public @interface DisabledOnMac {}
3.4.5 測試的元註解支持
你可以將大多數與測試相關的註解用作元註解,以創建自定義的組合註解,並減少整個測試套件中的重複配置。
你可以將以下各項用作與TestContext框架結合使用的元註解。
@BootstrapWith
@ContextConfiguration
@ContextHierarchy
@ActiveProfiles
@TestPropertySource
@DirtiesContext
@WebAppConfiguration
@TestExecutionListeners
@Transactional
@BeforeTransaction
@AfterTransaction
@Commit
@Rollback
@Sql
@SqlConfig
@SqlMergeMode
@SqlGroup
-
@Repeat
(僅支持 JUnit 4) -
@Timed
(僅支持 JUnit 4) -
@IfProfileValue
(僅支持 JUnit 4) -
@ProfileValueSourceConfiguration
(僅支持 JUnit 4) -
@SpringJUnitConfig
(僅支持 JUnit Jupiter) -
@SpringJUnitWebConfig
(僅支持JUnit Jupiter) -
@TestConstructor
(僅支持 JUnit Jupiter) -
@EnabledIf
(僅支持 JUnit Jupiter) -
@DisabledIf
(僅支持 JUnit Jupiter)
考慮以下示例:
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }
如果發現我們要在基於JUnit 4的測試套件中重複上述配置,則可以通過引入一個自定義的組合註解來減少重複,該註解集中了Spring的通用測試配置,如下所示:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
然後,我們可以使用我們的自定義@TransactionalDevTestConfig
的註解來簡化單個基於JUnit 4的測試類的配置,如下所示:
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class UserRepositoryTests { }
如果我們編寫使用JUnit Jupiter的測試,則可以進一步減少代碼重複,因為JUnit 5中的註解也可以用作元註解。考慮以下示例:
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
如果發現我們在基於JUnit Jupiter的測試套件中重複了前面的配置,則可以通過引入一個自定義組合註解來減少重複,該註解集中了Spring和JUnit Jupiter的通用測試配置,如下所示:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
然後,我們可以使用我們的自定義@TransactionalDevTestConfig
的註解來簡化基於單個JUnit Jupiter的測試類的配置,如下所示:
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
由於JUnit Jupiter支持使用@Test
、@RepeatedTest
、ParameterizedTest
和其他作為元註解,因此你也可以在測試方法級別創建自定義的組合註解。例如,如果我們希望創建一個組合的註解,將JUnit Jupiter的@Test
和@Tag
註解與Spring的@Transactional
註解相結合,則可以創建一個@TransactionalIntegrationTest
註解,如下所示:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
public @interface TransactionalIntegrationTest { }
然後,我們可以使用自定義的@TransactionalIntegrationTest
註解來簡化基於單個JUnit Jupiter的測試方法的配置,如下所示:
@TransactionalIntegrationTest
void saveOrder() { }
@TransactionalIntegrationTest
void deleteOrder() { }
有關更多詳細信息,請參見Spring Annotation編程模型Wiki頁。
作者
個人從事金融行業,就職過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就職於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公眾號和博客站點對知識體系進行分享。關注公眾號:青年IT男 獲取最新技術文章推送!
博客地址: http://youngitman.tech
CSDN: https://blog.csdn.net/liyong1028826685
微信公眾號:
技術交流群: