TestContext 框架支援類

本節描述了支援 Spring TestContext 框架的各種類。

Spring JUnit 4 Runner

Spring TestContext 框架透過自定義 runner (支援 JUnit 4.12 或更高版本) 與 JUnit 4 完全整合。透過使用 @RunWith(SpringJUnit4ClassRunner.class) 或更短的 @RunWith(SpringRunner.class) 變體註解測試類,開發人員可以實現標準的基於 JUnit 4 的單元測試和整合測試,同時享受 TestContext 框架的優勢,例如支援載入應用上下文、測試例項的依賴注入、事務性測試方法執行等。如果你想將 Spring TestContext 框架與另一個 runner (例如 JUnit 4 的 Parameterized runner) 或第三方 runner (例如 MockitoJUnitRunner) 一起使用,你可以選擇使用Spring 對 JUnit rules 的支援

以下程式碼清單顯示了配置測試類以使用自定義 Spring Runner 執行的最低要求

  • Java

  • Kotlin

@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {

	@Test
	public void testMethod() {
		// test logic...
	}
}
@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {

	@Test
	fun testMethod() {
		// test logic...
	}
}

在前面的示例中,@TestExecutionListeners 配置了一個空列表,以停用預設的監聽器,否則它們需要透過 @ContextConfiguration 配置一個 ApplicationContext

Spring JUnit 4 Rules

org.springframework.test.context.junit4.rules 包提供了以下 JUnit 4 rules (支援 JUnit 4.12 或更高版本)

  • SpringClassRule

  • SpringMethodRule

SpringClassRule 是一個支援 Spring TestContext 框架類級別特性的 JUnit TestRule,而 SpringMethodRule 是一個支援 Spring TestContext 框架例項級別和方法級別特性的 JUnit MethodRule

SpringRunner 不同,Spring 基於 rule 的 JUnit 支援的優勢在於它獨立於任何 org.junit.runner.Runner 實現,因此可以與現有的替代 runner (例如 JUnit 4 的 Parameterized) 或第三方 runner (例如 MockitoJUnitRunner) 結合使用。

為了支援 TestContext 框架的全部功能,你必須將 SpringClassRuleSpringMethodRule 結合使用。以下示例顯示了在整合測試中宣告這些 rules 的正確方法

  • Java

  • Kotlin

// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
public class IntegrationTest {

	@ClassRule
	public static final SpringClassRule springClassRule = new SpringClassRule();

	@Rule
	public final SpringMethodRule springMethodRule = new SpringMethodRule();

	@Test
	public void testMethod() {
		// test logic...
	}
}
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
class IntegrationTest {

	@Rule
	val springMethodRule = SpringMethodRule()

	@Test
	fun testMethod() {
		// test logic...
	}

	companion object {
		@ClassRule
		val springClassRule = SpringClassRule()
	}
}

JUnit 4 支援類

org.springframework.test.context.junit4 包為基於 JUnit 4 的測試用例提供了以下支援類 (支援 JUnit 4.12 或更高版本)

  • AbstractJUnit4SpringContextTests

  • AbstractTransactionalJUnit4SpringContextTests

AbstractJUnit4SpringContextTests 是一個抽象基測試類,它在 JUnit 4 環境中集成了 Spring TestContext 框架和顯式 ApplicationContext 測試支援。當你擴充套件 AbstractJUnit4SpringContextTests 時,你可以訪問一個 protectedapplicationContext 例項變數,你可以使用它來執行顯式的 bean 查詢或測試整個上下文的狀態。

AbstractTransactionalJUnit4SpringContextTestsAbstractJUnit4SpringContextTests 的抽象事務性擴充套件,它為 JDBC 訪問增加了一些便利功能。此類期望在 ApplicationContext 中定義一個 javax.sql.DataSource bean 和一個 PlatformTransactionManager bean。當你擴充套件 AbstractTransactionalJUnit4SpringContextTests 時,你可以訪問一個 protectedjdbcTemplate 例項變數,你可以使用它執行 SQL 語句來查詢資料庫。你可以使用這樣的查詢來確認在執行資料庫相關的應用程式程式碼之前和之後的資料庫狀態,並且 Spring 確保這些查詢在與應用程式程式碼相同的事務範圍內執行。與 ORM 工具一起使用時,請務必避免假陽性。正如JDBC 測試支援中所述,AbstractTransactionalJUnit4SpringContextTests 還提供了便利方法,這些方法使用上述 jdbcTemplate 委託給 JdbcTestUtils 中的方法。此外,AbstractTransactionalJUnit4SpringContextTests 提供了一個 executeSqlScript(..) 方法,用於針對配置的 DataSource 執行 SQL 指令碼。

這些類是為了擴充套件方便而提供的。如果你不想讓你的測試類繫結到 Spring 特定的類層次結構,你可以使用 @RunWith(SpringRunner.class)Spring 的 JUnit rules 來配置你自己的自定義測試類。

用於 JUnit Jupiter 的 SpringExtension

Spring TestContext 框架提供了與 JUnit Jupiter 測試框架 (JUnit 5 中引入) 的完全整合。透過使用 @ExtendWith(SpringExtension.class) 註解測試類,你可以實現標準的基於 JUnit Jupiter 的單元測試和整合測試,同時享受 TestContext 框架的優勢,例如支援載入應用上下文、測試例項的依賴注入、事務性測試方法執行等。

此外,由於 JUnit Jupiter 豐富的擴充套件 API,Spring 提供了以下超出 Spring 對 JUnit 4 和 TestNG 支援的功能

  • 對測試建構函式、測試方法和測試生命週期回撥方法的依賴注入。有關更多詳細資訊,請參見使用 SpringExtension 進行依賴注入

  • 強大的基於 SpEL 表示式、環境變數、系統屬性等的條件測試執行支援。有關更多詳細資訊和示例,請參見Spring JUnit Jupiter 測試註解中關於 @EnabledIf@DisabledIf 的文件。

  • 組合 Spring 和 JUnit Jupiter 註解的自定義組合註解。有關更多詳細資訊,請參見測試的元註解支援@TransactionalDevTestConfig@TransactionalIntegrationTest 的示例。

以下程式碼清單顯示瞭如何配置測試類以結合 @ContextConfiguration 使用 SpringExtension

  • Java

  • Kotlin

// Instructs JUnit Jupiter to extend the test with Spring support.
@ExtendWith(SpringExtension.class)
// Instructs Spring to load an ApplicationContext from TestConfig.class
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {

	@Test
	void testMethod() {
		// test logic...
	}
}
// Instructs JUnit Jupiter to extend the test with Spring support.
@ExtendWith(SpringExtension::class)
// Instructs Spring to load an ApplicationContext from TestConfig::class
@ContextConfiguration(classes = [TestConfig::class])
class SimpleTests {

	@Test
	fun testMethod() {
		// test logic...
	}
}

由於你還可以在 JUnit 5 中將註解用作元註解,Spring 提供了 @SpringJUnitConfig@SpringJUnitWebConfig 組合註解來簡化測試 ApplicationContext 和 JUnit Jupiter 的配置。

以下示例使用 @SpringJUnitConfig 來減少先前示例中使用的配置量

  • Java

  • Kotlin

// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load an ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {

	@Test
	void testMethod() {
		// test logic...
	}
}
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load an ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig::class)
class SimpleTests {

	@Test
	fun testMethod() {
		// test logic...
	}
}

類似地,以下示例使用 @SpringJUnitWebConfig 建立一個 WebApplicationContext 以供 JUnit Jupiter 使用

  • Java

  • Kotlin

// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load a WebApplicationContext from TestWebConfig.class
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {

	@Test
	void testMethod() {
		// test logic...
	}
}
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load a WebApplicationContext from TestWebConfig::class
@SpringJUnitWebConfig(TestWebConfig::class)
class SimpleWebTests {

	@Test
	fun testMethod() {
		// test logic...
	}
}

有關更多詳細資訊,請參見Spring JUnit Jupiter 測試註解中關於 @SpringJUnitConfig@SpringJUnitWebConfig 的文件。

使用 SpringExtension 進行依賴注入

SpringExtension 實現了 JUnit Jupiter 的 ParameterResolver 擴充套件 API,這使得 Spring 可以為測試建構函式、測試方法和測試生命週期回撥方法提供依賴注入。

具體來說,SpringExtension 可以將測試 ApplicationContext 中的依賴項注入到使用 Spring 的 @BeforeTransaction@AfterTransaction 或 JUnit 的 @BeforeAll, @AfterAll, @BeforeEach, @AfterEach, @Test, @RepeatedTest, @ParameterizedTest 等註解的測試建構函式和方法中。

建構函式注入

如果 JUnit Jupiter 測試類的建構函式中的特定引數型別為 ApplicationContext (或其子型別),或者使用了 @Autowired, @Qualifier, 或 @Value 註解或元註解,Spring 會將該特定引數的值注入為測試 ApplicationContext 中的相應 bean 或值。

如果測試類的建構函式被認為是可自動裝配的,Spring 也可以配置為自動裝配建構函式的所有引數。如果滿足以下條件之一(按優先順序順序),則建構函式被認為是可自動裝配的。

  • 建構函式使用 @Autowired 註解。

  • 測試類上存在或元存在 @TestConstructor,且其 autowireMode 屬性設定為 ALL

  • 預設的測試建構函式自動裝配模式已更改為 ALL

有關 @TestConstructor 的使用以及如何更改全域性測試建構函式自動裝配模式的詳細資訊,請參見@TestConstructor

如果測試類的建構函式被認為是可自動裝配的,Spring 將負責解析建構函式中所有引數的引數。因此,註冊到 JUnit Jupiter 的任何其他 ParameterResolver 都無法解析此類建構函式的引數。

如果使用 @DirtiesContext 在測試方法之前或之後關閉測試的 ApplicationContext,則測試類的建構函式注入不得與 JUnit Jupiter 的 @TestInstance(PER_CLASS) 支援結合使用。

原因是 @TestInstance(PER_CLASS) 指示 JUnit Jupiter 在測試方法呼叫之間快取測試例項。因此,測試例項將保留對最初從隨後已關閉的 ApplicationContext 中注入的 bean 的引用。由於測試類的建構函式在這種情況下只會呼叫一次,依賴注入將不會再次發生,隨後的測試將與已關閉的 ApplicationContext 中的 bean 互動,這可能導致錯誤。

要將 @DirtiesContext 與“在測試方法之前”或“在測試方法之後”模式結合使用,並且同時使用 @TestInstance(PER_CLASS),必須將 Spring 中的依賴項配置為透過欄位或 setter 注入提供,以便它們可以在測試方法呼叫之間重新注入。

在以下示例中,Spring 將從 TestConfig.class 載入的 ApplicationContext 中的 OrderService bean 注入到 OrderServiceIntegrationTests 建構函式中。

  • Java

  • Kotlin

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

	private final OrderService orderService;

	@Autowired
	OrderServiceIntegrationTests(OrderService orderService) {
		this.orderService = orderService;
	}

	// tests that use the injected OrderService
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){
	// tests that use the injected OrderService
}

請注意,此功能允許測試依賴項為 final,因此是不可變的。

如果 spring.test.constructor.autowire.mode 屬性設定為 all (參見@TestConstructor),我們可以在先前示例中省略建構函式上的 @Autowired 宣告,結果如下。

  • Java

  • Kotlin

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

	private final OrderService orderService;

	OrderServiceIntegrationTests(OrderService orderService) {
		this.orderService = orderService;
	}

	// tests that use the injected OrderService
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests(val orderService:OrderService) {
	// tests that use the injected OrderService
}

方法注入

如果 JUnit Jupiter 測試方法或測試生命週期回撥方法中的引數型別為 ApplicationContext (或其子型別),或者使用了 @Autowired, @Qualifier, 或 @Value 註解或元註解,Spring 會將該特定引數的值注入為測試 ApplicationContext 中的相應 bean。

在以下示例中,Spring 將從 TestConfig.class 載入的 ApplicationContext 中的 OrderService 注入到 deleteOrder() 測試方法中

  • Java

  • Kotlin

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

	@Test
	void deleteOrder(@Autowired OrderService orderService) {
		// use orderService from the test's ApplicationContext
	}
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {

	@Test
	fun deleteOrder(@Autowired orderService: OrderService) {
		// use orderService from the test's ApplicationContext
	}
}

由於 JUnit Jupiter 中 ParameterResolver 支援的強大性,你還可以在一個方法中注入多個依賴項,不僅來自 Spring,還可以來自 JUnit Jupiter 本身或其他第三方擴充套件。

以下示例顯示了 Spring 和 JUnit Jupiter 如何同時將依賴項注入到 placeOrderRepeatedly() 測試方法中。

  • Java

  • Kotlin

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

	@RepeatedTest(10)
	void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
			@Autowired OrderService orderService) {

		// use orderService from the test's ApplicationContext
		// and repetitionInfo from JUnit Jupiter
	}
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {

	@RepeatedTest(10)
	fun placeOrderRepeatedly(repetitionInfo:RepetitionInfo, @Autowired orderService:OrderService) {

		// use orderService from the test's ApplicationContext
		// and repetitionInfo from JUnit Jupiter
	}
}

請注意,使用 JUnit Jupiter 的 @RepeatedTest 使測試方法能夠訪問 RepetitionInfo

@Nested 測試類配置

Spring TestContext 框架支援在 JUnit Jupiter 中的 @Nested 測試類上使用測試相關注解,包括對從 enclosing 類繼承測試類配置的一流支援,並且這種配置將預設繼承。要將預設的 INHERIT 模式更改為 OVERRIDE 模式,你可以在單個 @Nested 測試類上使用 @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE) 註解。顯式的 @NestedTestConfiguration 宣告將應用於被註解的測試類及其任何子類和巢狀類。因此,你可以在頂層測試類上使用 @NestedTestConfiguration 註解,它將遞迴地應用於其所有巢狀測試類。

如果您正在開發與 Spring TestContext Framework 整合並需要在封閉類層次結構中支援註解繼承的元件,則必須使用 TestContextAnnotationUtils 中提供的註解搜尋工具,以便遵循 @NestedTestConfiguration 語義。

為了允許開發團隊將預設設定更改為 OVERRIDE(例如,為了與 Spring Framework 5.0 至 5.2 相容),可以透過 JVM 系統屬性或類路徑根目錄下的 spring.properties 檔案來全域性更改預設模式。有關詳細資訊,請參見“更改預設封閉配置繼承模式”說明。

儘管下面的“Hello World”示例非常簡單,但它展示瞭如何在頂級類上宣告通用配置,該配置會被其 @Nested 測試類繼承。在此特定示例中,只有 TestConfig 配置類被繼承。每個巢狀測試類提供自己的一組活動配置檔案,從而導致每個巢狀測試類有不同的 ApplicationContext(詳情請參見上下文快取)。請查閱支援的註解列表,檢視哪些註解可以在 @Nested 測試類中繼承。

  • Java

  • Kotlin

@SpringJUnitConfig(TestConfig.class)
class GreetingServiceTests {

	@Nested
	@ActiveProfiles("lang_en")
	class EnglishGreetings {

		@Test
		void hello(@Autowired GreetingService service) {
			assertThat(service.greetWorld()).isEqualTo("Hello World");
		}
	}

	@Nested
	@ActiveProfiles("lang_de")
	class GermanGreetings {

		@Test
		void hello(@Autowired GreetingService service) {
			assertThat(service.greetWorld()).isEqualTo("Hallo Welt");
		}
	}
}
@SpringJUnitConfig(TestConfig::class)
class GreetingServiceTests {

	@Nested
	@ActiveProfiles("lang_en")
	inner class EnglishGreetings {

		@Test
		fun hello(@Autowired service:GreetingService) {
			assertThat(service.greetWorld()).isEqualTo("Hello World")
		}
	}

	@Nested
	@ActiveProfiles("lang_de")
	inner class GermanGreetings {

		@Test
		fun hello(@Autowired service:GreetingService) {
			assertThat(service.greetWorld()).isEqualTo("Hallo Welt")
		}
	}
}

TestNG 支援類

org.springframework.test.context.testng 包為基於 TestNG 的測試用例提供了以下支援類

  • AbstractTestNGSpringContextTests

  • AbstractTransactionalTestNGSpringContextTests

AbstractTestNGSpringContextTests 是一個抽象基礎測試類,它在 TestNG 環境中將 Spring TestContext Framework 與顯式的 ApplicationContext 測試支援整合。當您繼承 AbstractTestNGSpringContextTests 時,您可以訪問一個 protected applicationContext 例項變數,您可以使用它來執行顯式的 Bean 查詢或測試整個上下文的狀態。

AbstractTransactionalTestNGSpringContextTestsAbstractTestNGSpringContextTests 的一個抽象事務性擴充套件,它為 JDBC 訪問添加了一些便利功能。此類需要在一個 ApplicationContext 中定義一個 javax.sql.DataSource Bean 和一個 PlatformTransactionManager Bean。當您繼承 AbstractTransactionalTestNGSpringContextTests 時,您可以訪問一個 protected jdbcTemplate 例項變數,您可以使用它執行 SQL 語句來查詢資料庫。您可以使用此類查詢來確認執行資料庫相關應用程式程式碼之前和之後的資料庫狀態,並且 Spring 確保此類查詢在與應用程式程式碼相同的事務範圍內執行。與 ORM 工具結合使用時,務必避免假陽性。正如在JDBC 測試支援中提到的,AbstractTransactionalTestNGSpringContextTests 也提供了委託給 JdbcTestUtils 中方法呼叫的便利方法,透過使用上述的 jdbcTemplate。此外,AbstractTransactionalTestNGSpringContextTests 提供了 executeSqlScript(..) 方法,用於針對配置的 DataSource 執行 SQL 指令碼。

這些類提供了方便擴充套件的方式。如果您不想讓您的測試類繫結到 Spring 特定的類層次結構,您可以透過使用 @ContextConfiguration@TestExecutionListeners 等註解以及手動使用 TestContextManager 引入您的測試類來配置自己的自定義測試類。請參閱 AbstractTestNGSpringContextTests 的原始碼,獲取如何引入您的測試類的示例。