Spring 5 中文解析測試篇-集成測試之概要和註解

本節(本章其他部分)涵蓋了Spring應用程序的集成測試。它包括如下主題:html

3.1 概要

可以執行一些集成測試而無需部署到應用程序服務器或鏈接到其餘企業基礎結構,這一點很重要。這樣能夠測試如下內容:java

  • 正確鏈接Spring IoC容器上下文。
  • 使用JDBC或ORM工具進行數據訪問。這能夠包括諸如SQL語句的正確性、Hibernate查詢、JPA實體映射之類的東西。

Spring框架爲Spring測試模塊中的集成測試提供了一流的支持。實際的JAR文件的名稱可能包括髮行版,也可能採用長org.springframework.test格式,具體取決於你從何處獲取(請參閱「依賴管理」部分中的說明)。該庫包含org.springframework.test包,其中包含用於與Spring容器進行集成測試的有價值的類。此測試不依賴於應用程序服務器或其餘部署環境。此類測試的運行速度比單元測試慢,但比依賴於部署到應用程序服務器的等效Selenium測試或遠程測試快。git

單元和集成測試支持以註解驅動的Spring TestContext 框架的形式提供。TestContext框架與實際使用的測試框架無關,該框架容許各類環境(包括JUnit,TestNG和其餘環境)中對測試進行檢測。github

3.2 集成測試目標

Spring的集成測試支持的主要目標以下:web

接下來的幾節描述了每一個目標,並提供了有關實現和配製詳細信息的連接。算法

Fixture意思:JUnit提供了編寫測試前準備、測試後清理的固定代碼,咱們稱之爲 Fixture
3.2.1 上下文管理和緩存

Spring TestContext 框架提供了Spring ApplicationContext實例和WebApplicationContext實例的一致加載以及這些上下文的緩存。支持加載上下文的緩存很重要,由於啓動時間可能會成爲一個問題-不是由於Spring自己的開銷,而是由於Spring容器實例化的對象須要時間才能實例化。例如,具備50到100個Hibernate映射文件的項目可能須要10到20秒來加載映射文件,而且在每一個測試fixture中運行每一個測試以前要承擔該消耗,這會致使總體測試運行速度變慢,從而下降了開發人員的工做效率。spring

測試類一般聲明XML或Groovy配置元數據的資源位置數組(一般是在類路徑中)或用於配置應用程序的組件類的數組。這些位置或類與web.xml或其餘用於生產部署的配置文件中指定的位置或類相同或類似。sql

默認狀況下,加載後,已配置的ApplicationContext將從新用每一個測試。所以,每一個測試套件僅產生一次安裝成本,而且隨後的測試執行要快得多。在這種狀況下,術語「測試套件」是指全部測試都在相同JVM中運行,例如,全部測試都從給定項目或模塊的AntMavenGradle構建運行。在不太可能的狀況下,測試破壞了應用程序上下文並須要從新加載(例如,經過修改bean定義或應用程序對象的狀態),能夠將TestContext框架配置爲從新加載配置並重建應用程序上下文,而後再執行下一個測試。數據庫

請參見使用TestContext框架進行上下文管理上下文緩存express

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子句從給定表中刪除行。
AbstractTransactionalJUnit4SpringContextTestsAbstractTransactionalTestNGSpringContextTests提供了便利的方法,這些方法委託給 JdbcTestUtils中的上述方法。 spring-jdbc模塊提供了對配置和啓動嵌入式數據庫的支持你能夠在與數據庫交互的集成測試中使用它。有關詳細信息,請參見 嵌入式數據庫支持和使用 嵌入式數據庫測試數據訪問邏輯。
3.4 註解

本節介紹了在測試Spring應用程序時可使用的註解。它包括如下主題:

3.4.1 Spring測試註解

Spring框架提供瞭如下特定於Spring的註解集,你能夠在單元測試和集成測試中將它們與TestContext框架結合使用。有關更多信息,請參見相應的javadoc,包括默認屬性值、屬性別名和其餘詳細信息。

Spring的測試註解包括如下內容:

@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...
}
  1. 引用XML文件。

如下示例顯示了一個@ContextConfiguration註解,該註解引用了一個類:

@ContextConfiguration(classes = TestConfig.class) //1
class ConfigClassApplicationContextTests {
    // class body...
}
  1. 引用類文件
參考代碼: 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...
}
  1. 配置位置和自定義加載器。
@ContextConfiguration爲繼承資源位置或配置類以及超類聲明上下文初始化器提供支持。

有關更多詳細信息,請參見上下文管理@ContextConfiguration javadocs。

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

@WebAppConfiguration

@WebAppConfiguration是一個類級別的註解,可用於聲明爲集成測試加載的ApplicationContext應該是WebApplicationContext@WebAppConfiguration僅存在於測試類上,能夠確保爲測試加@WebApplicationContext,並使用默認值file:src/main/webapp做爲Web應用程序根目錄(也就是即資源基本路徑)。資源基礎路徑用於在後臺建立MockServletContext,該MockServletContext用做測試的WebApplicationContextServletContext

如下示例顯示瞭如何使用@WebAppConfiguration註解:

@ContextConfiguration
@WebAppConfiguration //
class WebAppTests {
    // class body...
}

要覆蓋默認值,可使用隱式值屬性指定其餘基礎資源路徑。classpath:file:資源前綴均受支持。若是未提供資源前綴,則假定該路徑是文件系統資源。如下示例顯示如何指定類路徑資源:

@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources") //1
class WebAppTests {
    // class body...
}
  1. 指定類路徑資源。

注意,@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...
}
  1. 指示開發配置文件應處於活動狀態。

如下示例代表devintegration配置文件均應處於活動狀態:

@ContextConfiguration
@ActiveProfiles({"dev", "integration"}) //1
class DeveloperIntegrationTests {
    // class body...
}
  1. 指示devintegration配置文件應該處於活動狀態。
@ActiveProfiles提供了對繼承默認狀況下超類聲明的活動bean定義配置文件的支持。你還能夠經過實現自定義 ActiveProfilesResolver並使用 @ActiveProfilesresolver屬性對其進行註冊,以編程方式解析活動bean定義配置文件。

參見環境配置文件和@ActiveProfiles javadoc的上下文配置以得到示例和更多細節。

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

@TestPropertySource

@TestPropertySource是類級別的註解,可用於配置屬性文件和內聯屬性的位置,這些屬性和內聯屬性將被添加到環境中針對集成測試加載的ApplicationContextPropertySources集中。

下面的示例演示如何從類路徑聲明屬性文件:

@ContextConfiguration
@TestPropertySource("/test.properties") //1
class MyIntegrationTests {
    // class body...
}
  1. 從類路徑根目錄中的test.properties獲取屬性。

下面的示例演示如何聲明內聯屬性:

@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" }) //1
class MyIntegrationTests {
    // class body...
}
  1. 聲明時區和端口屬性。

有關示例和更多詳細信息,請參見使用測試屬性源進行上下文配置

@DynamicPropertySource

@DynamicPropertySource是方法級別的註解,可用於註冊動態屬性,以將動態屬性添加到環境中針對集成測試加載的ApplicationContextPropertySources集中。當你不預先知道屬性的值時,例如,若是屬性是由外部資源管理的,例如由Testcontainers項目管理的容器,則動態屬性頗有用。

下面的示例演示如何註冊動態屬性:

@ContextConfiguration
class MyIntegrationTests {

    static MyExternalServer server = // ...

    @DynamicPropertySource //1
    static void dynamicProperties(DynamicPropertyRegistry registry) { //2
        registry.add("server.port", server::getPort); //3
    }

    // tests ...
}
  1. 使用@DynamicPropertySource註解靜態方法。
  2. 接受DynamicPropertyRegistry做爲參數。
  3. 註冊要從服務器延遲檢索的動態server.port屬性。

有關更多詳細信息,請參見使用動態屬性源進行上下文配置

@DirtiesContext

@DirtiesContext表示底層的Spring ApplicationContext在執行測試期間已被清理(即,該測試以某種方式修改或破壞了它(例如,經過更改單例bean的狀態)),應將其關閉。當應用程序上下文被標記爲清理時,它將從測試框架的緩存中刪除並關閉。所以,對於須要具備相同配置元數據的上下文的後續測試,將從新構建底層Spring容器。

你能夠將@DirtiesContext用做同一類或類層次結構中的類級別和方法級別的註解。在這種狀況下,取決於配置的methodModeclassMode,在任何此類帶註解的方法以前或以後以及當前測試類以前或以後,ApplicationContext均標記爲清理。

如下示例說明了在各類配置狀況下什麼時候清理上下文:

  • 在當前測試類以前,在類模式設置爲BEFORE_CLASS的類上聲明時。

    @DirtiesContext(classMode = BEFORE_CLASS) //1
    class FreshContextTests {
        // some tests that require a new Spring container
    }
    1. 在當前測試類以前清理上下文。
  • 在當前測試類以後,當在類模式設置爲AFTER_CLASS(即默認類模式)的類上聲明時。

    @DirtiesContext 
    class ContextDirtyingTests {
        // some tests that result in the Spring container being dirtied
    }
    1. 當前測試類以後清理上下文。
  • 在當前測試類中的每一個測試方法以後,在類模式設置爲AFTER_EACH_TEST_METHOD的類上聲明時。

    @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) 
    class ContextDirtyingTests {
        // some tests that result in the Spring container being dirtied
    }
    1. 每種測試方法後清理上下文。
  • 在當前測試以前,當在方法模式設置爲BEFORE_METHOD的方法上聲明時。

    @DirtiesContext(methodMode = BEFORE_METHOD) //1
    @Test
    void testProcessWhichRequiresFreshAppCtx() {
        // some logic that requires a new Spring container
    }
    1. 在當前測試方法以前清理上下文。
  • 當前測試以後,當在方法模式設置爲AFTER_METHOD的方法上聲明時(即默認方法模式)。

    @DirtiesContext //1
    @Test
    void testProcessWhichDirtiesAppCtx() {
        // some logic that results in the Spring container being dirtied
    }
    1. 當前測試方法後清理上下文。

若是在使用@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
    }
}
  1. 使用當前級別的算法。

有關EXHAUSTIVECURRENT_LEVEL算法的更多詳細信息,請參見DirtiesContext.HierarchyMode javadoc。

@TestExecutionListeners

@TestExecutionListeners定義了用於配置應在TestContextManager中註冊的TestExecutionListener實現的類級元數據。一般,@TestExecutionListeners@ContextConfiguration結合使用。

下面的示例演示如何註冊兩個TestExecutionListener實現:

@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) //1
class CustomTestExecutionListenerTests {
    // class body...
}
  1. 註冊兩個TestExecutionListener實現。

默認狀況下,@TestExecutionListeners支持繼承的監聽器。有關示例和更多詳細信息,請參見javadoc

@Commit

@Commit表示應在測試方法完成後提交用於事務性測試方法的事務。你能夠將@Commit用做@Rollback(false)的直接替代品,以更明確地傳達代碼的意圖。與@Rollback相似,@ Commit也能夠聲明爲類級別或方法級別的註解。

如下示例顯示瞭如何使用@Commit註解:

@Commit //1
@Test
void testProcessWithoutRollback() {
    // ...
}
  1. 將測試結果提交到數據庫。

@Rollback

@Rollback表示在測試方法完成後是否應回退用於事務性測試方法的事務。若是爲true,則回滾該事務。不然,將提交事務(另請參見@Commit)。即便未明確聲明@Rollback,Spring TestContext框架中用於集成測試的回滾默認爲true。

當聲明爲類級註解時,@Rollback定義測試類層次結構中全部測試方法的默認回滾語義。當聲明爲方法級別的註解時,@Rollback定義特定測試方法的回滾語義,從而可能覆蓋類級別的@Rollback@Commit語義。

如下示例使測試方法的結果不回滾(即,結果已提交到數據庫):

@Rollback(false) //1
@Test
void testProcessWithoutRollback() {
    // ...
}
  1. 不要回滾結果。

@BeforeTransaction

@BeforeTransaction表示,對於已配置爲使用Spring的@Transactional註解在事務內運行的測試方法,帶註解的void方法應在事務開始以前運行。@BeforeTransaction方法不須要public訪問限定,能夠在基於Java 8的接口默認方法。

如下示例顯示瞭如何使用@BeforeTransaction註解:

@BeforeTransaction //1
void beforeTransaction() {
    // logic to be executed before a transaction is started
}
  1. 在事務以前運行此方法。

@AfterTransaction

@AfterTransaction表示,對於已配置爲經過使用Spring的@Transactional註解在事務內運行的測試方法,帶註解的void方法應在事務結束後運行。@AfterTransaction方法不須要public訪問限定,能夠在基於Java 8的接口默認方法中聲明。

@AfterTransaction //1
void afterTransaction() {
    // logic to be executed after a transaction has ended
}
  1. 事務後運行此方法。

@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
}
  1. 運行此測試的兩個腳本。

有關更多詳細信息,請參見使用@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
}
  1. 在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
    }
}
  1. 對於類中的全部測試方法,將@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
}
  1. 聲明一組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
}
  1. 僅當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
}
  1. 對單元測試和集成測試運行此測試。

@ProfileValueSourceConfiguration

@ProfileValueSourceConfiguration是一個類級別的註解,它指定檢索經過@IfProfileValue註解配置的配置文件值時要使用哪一種ProfileValueSource類型。若是未爲測試聲明@ProfileValueSourceConfiguration,則默認使用SystemProfileValueSource。如下示例顯示瞭如何使用@ProfileValueSourceConfiguration

@ProfileValueSourceConfiguration(CustomProfileValueSource.class) //1
public class CustomProfileValueSourceTests {
    // class body...
}
  1. 使用自定義配置文件值源。
參考代碼: 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
}
  1. 將測試時間設置爲一秒。

Spring的@Timed註解與JUnit 4的@Test(timeout = ...)支持具備不一樣的語義。具體來講,因爲JUnit 4處理測試執行超時的方式(即經過在單獨的線程中執行測試方法),若是測試花費的時間太長,@Test(timeout = ...)會搶先經過測試。另外一方面,Spring的@Timed不會搶先經過測試,而是在失敗以前等待測試完成。

@Repeat

@Repeat表示必須重複運行帶註解的測試方法。註解中指定了要執行測試方法的次數。重複執行的範圍包括測試方法自己的執行以及測試套件中任何安裝或拆除。如下示例顯示瞭如何使用@Repeat註解:

@Repeat(10) //1
@Test
public void testProcessRepeatedly() {
    // ...
}
  1. 重複此測試十次。
3.4.4 Spring JUnit Jupiter測試註解

如下註解僅在與SpringExtensionJUnit 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...
}
  1. 指定配置類。

如下示例顯示如何使用@SpringJUnitConfig註解指定配置文件的位置:

@SpringJUnitConfig(locations = "/test-config.xml") //1
class XmlJUnitJupiterSpringTests {
    // class body...
}
  1. 指定配置文件的位置。

有關更多詳細信息,請參見上下文管理以及@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...
}
  1. 指定配置類。

如下示例顯示如何使用@SpringJUnitWebConfig註解指定配置文件的位置:

@SpringJUnitWebConfig(locations = "/test-config.xml") //1
class XmlJUnitJupiterSpringWebTests {
    // class body...
}
  1. 指定配置文件的位置。

有關更多詳細信息,請參見上下文管理以及@SpringJUnitWebConfig@ContextConfiguration@WebAppConfiguration的javadoc。

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

@TestConstructor

@TestConstructor是類型級別的註解,用於配置如何從測試的ApplicationContext中的組件自動鏈接測試類構造函數的參數。

若是在測試類上不存在@TestConstructormeta-present,則將使用默認的測試構造函數自動裝配模式。有關如何更改默認模式的詳細信息,請參見下面的提示。可是請注意,構造函數上的@Autowired本地聲明優先於@TestConstructor和默認模式。

更改默認的測試構造函數自動裝配模式

能夠經過將JVM系統屬性spring.test.constructor.autowire.mode設置爲all來更改默認的測試構造函數自動裝配模式。或者,能夠經過SpringProperties機制更改默認模式。

若是未設置spring.test.constructor.autowire.mode屬性,則不會自動裝配測試類構造函數。

從Spring框架5.2開始,僅將@TestConstructorSpringExtension結合使用以與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@RepeatedTestParameterizedTest和其餘做爲元註解,所以你也能夠在測試方法級別建立自定義的組合註解。例如,若是咱們但願建立一個組合的註解,將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/liyong1...

微信公衆號:

技術交流羣:

相關文章
相關標籤/搜索