TestContext Framework Support Classes

本部分描述支持 Spring TestContext Framework 的各种类。

Spring JUnit 4 Runner

Spring TestContext框架通过自定义运行器(在JUnit 4.12或更高版本上受支持)与JUnit 4完全集成。通过使用`@RunWith(SpringJUnit4ClassRunner.class)或更短的@RunWith(SpringRunner.class)变体注解测试类,开发人员可以实现基于JUnit 4的标准单元和集成测试,同时充分利用TestContext框架的优势,例如支持加载应用程序上下文、测试实例的依赖项注入、事务测试方法执行等。如果你想将Spring TestContext Framework与替代运行器(例如JUnit 4的`Parameterized`运行器)或第三方运行器(例如`MockitoJUnitRunner)一起使用,你可以选择使用Spring’s support for 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 规则(在 JUnit 4.12 或更高版本上受支持):

  • SpringClassRule

  • SpringMethodRule

“SpringClassRule” 是 JUnit “TestRule”,支持 Spring TestContext Framework 的类级特性,而 “SpringMethodRule” 是 JUnit “MethodRule”,支持 Spring TestContext Framework 的实例级和方法级特性。

与 “SpringRunner” 相比,Spring 基于规则的 JUnit 支持具有独立于任何 “org.junit.runner.Runner” 实现的优势,因此可以与现有的其他运行器(例如 JUnit 4 的 “Parameterized”)或第三方运行器(例如 “MockitoJUnitRunner”)结合使用。

为了支持 TestContext 框架的全部功能,你必须将 “SpringClassRule” 与 “SpringMethodRule” 结合使用。以下示例显示在集成测试中声明这些规则的正确方法:

  • 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 Support Classes

“org.springframework.test.context.junit4” 包为基于 JUnit 4 的测试用例提供以下支持类(在 JUnit 4.12 或更高版本上受支持):

  • AbstractJUnit4SpringContextTests

  • AbstractTransactionalJUnit4SpringContextTests

“AbstractJUnit4SpringContextTests” 是一个抽象基础测试类,它在 JUnit 4 环境中集成了具有显式 “ApplicationContext” 测试支持的 Spring TestContext Framework。当你扩展 “AbstractJUnit4SpringContextTests” 时,你可以访问一个 “protected” “applicationContext” 实例变量,可以使用该变量执行显式 Bean 查找或测试上下文的整体状态。

`AbstractTransactionalJUnit4SpringContextTests`是`AbstractJUnit4SpringContextTests`的抽象事务扩展,为JDBC访问添加了一些便捷功能。此类期望在`ApplicationContext`中定义`javax.sql.DataSource`bean和`PlatformTransactionManager`bean。在扩展`AbstractTransactionalJUnit4SpringContextTests`时,你可以访问`protected``jdbcTemplate`实例变量,可使用它来运行查询数据库的SQL语句。你可以使用此类查询来确认在运行与数据库相关的应用程序代码之前和之后的状态,并且Spring会确保此类查询在与应用程序代码相同的事务范围内运行。在与ORM工具结合使用时,请务必避免使用false positives。如JDBC Testing Support中所述,`AbstractTransactionalJUnit4SpringContextTests`还提供了一些便利方法,通过使用上述`jdbcTemplate`来委托给`JdbcTestUtils`中的方法。此外,`AbstractTransactionalJUnit4SpringContextTests`提供了一个`executeSqlScript(..)`方法,用于针对配置的`DataSource`运行SQL脚本。

这些类可方便于扩展。如果您不希望测试类绑定到 Spring 特定的类层次结构,则可以使用 @RunWith(SpringRunner.class)Spring’s JUnit rules 配置自己的自定义测试类。

SpringExtension for JUnit Jupiter

Spring TestContext Framework 提供与 JUnit 5 中引入的 JUnit Jupiter 测试框架的完全集成。通过用 “@ExtendWith(SpringExtension.class)” 对测试类进行注释,你可以实现基于 JUnit Jupiter 的标准单元和集成测试,同时享受 TestContext 框架带来的好处,例如加载应用程序上下文、测试实例的依赖注入、事务性测试方法执行等的支持。

此外,由于 JUnit Jupiter 中丰富的扩展 API,Spring 提供了以下特性,超出了 Spring 对 JUnit 4 和 TestNG 支持的特性集:

以下代码清单展示了如何配置一个测试类以将`SpringExtension`与 @ContextConfiguration 结合使用:

  • 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 为 JUnit Jupiter 创建一个`WebApplicationContext`:

  • 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 Testing Annotations@SpringJUnitConfig@SpringJUnitWebConfig 的文档,以了解详细信息。

Dependency Injection with the SpringExtension

SpringExtension 实现了 JUnit Jupiter 的 ParameterResolver 扩展 API,它允许 Spring 为测试构造函数、测试方法和测试生命周期回调方法提供依赖项注入。

具体来说,SpringExtension 可以从测试的`ApplicationContext` 中向使用 Spring 的 @BeforeTransaction@AfterTransaction 或 JUnit 的 @BeforeAll@AfterAll@BeforeEach@AfterEach@Test@RepeatedTest@ParameterizedTest 等进行注释或元注释的测试构造函数和方法注入依赖项。

Constructor Injection

如果 JUnit Jupiter 测试类的构造函数中的特定参数是类型为 ApplicationContext(或其子类型)的或者使用 @Autowired@Qualifier@Value 进行了注释或元注释,Spring 将使用测试的 ApplicationContext 中对应的 bean 或值注入该特定参数的值。

如果构造函数被认为是 autowirable 的,还可以将 Spring 配置为自动注入测试类构造函数的所有参数。如果满足以下条件之一(按优先顺序),则构造函数被认为是可自动注入的。

  • 构造函数使用 @Autowired 进行了注解。

  • 在测试类中使用 autowireMode 属性集 ALL 出现或元出现 @TestConstructor

  • 默认 test constructor autowire mode 已更改为 ALL

请参阅@TestConstructor以了解`@TestConstructor`的使用详情以及如何更改全局_test constructor autowire mode_。

如果测试类的构造函数被视为 autowirable,Spring 会负责解析构造函数中所有参数的参数。因此,用 JUnit Jupiter 注册的其他 ParameterResolver 无法解析此类构造函数的参数。

如果使用 @DirtiesContext 在测试方法之前或之后关闭测试的 ApplicationContext,则测试类的构造函数注入不得与 JUnit Jupiter 的 @TestInstance(PER_CLASS) 支持一起使用。 原因是 @TestInstance(PER_CLASS) 指示 JUnit Jupiter 在测试方法调用之间缓存测试实例。因此,测试实例将保留对最初从已随后关闭的 ApplicationContext 注入的 bean 的引用。由于在这种情况下测试类的构造函数只会调用一次,因此依赖项注入不会再次发生,并且后续测试将与已关闭的 ApplicationContext 中的 bean 交互,这可能会导致错误。 要在“测试方法之前”或“测试方法之后”模式下与 @TestInstance(PER_CLASS) 结合使用 @DirtiesContext,必须配置 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
}

Method Injection

如果 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 test class configuration

Spring 测试上下文框架 从 Spring Framework 5.0 开始,支持在 JUnit Jupiter 的 @Nested 测试类上使用与测试相关的注解;然而,直到 Spring Framework 5.3,类级别的测试配置注解才不会像从超类继承的那样,从封闭类中`继承`。

Spring Framework 5.3 引入了从封闭类中继承测试类配置的一流支持,并且此类配置将被默认继承。要从默认的 INHERIT 模式更改为 OVERRIDE 模式,可以使用 @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE) 为单独的 @Nested 测试类添加注解。明确的 @NestedTestConfiguration 声明将应用于带有注解的测试类以及它的任何子类和嵌套类。因此,可以用 @NestedTestConfiguration 为顶级测试类添加注解,并且会在嵌套它的所有测试类中递归地应用它。

为了允许开发团队更改为`OVERRIDE`的默认值——例如,为了与Spring Framework 5.0到5.2兼容——可以通过JVM系统属性或类路径根目录中的`spring.properties`文件在全局范围内更改默认模式。请参阅"Changing the default enclosing configuration inheritance mode"说明以了解详情。

尽管以下“Hello World”示例非常简单,但它展示了如何在顶级类上声明公共配置,该类由其`@Nested`测试类继承。在此特定示例中,仅继承了`TestConfig`配置类。每个嵌套测试类都提供其自己的一组活动配置文件,从而为每个嵌套测试类生成了一个不同的`ApplicationContext`(请参阅Context Caching以了解详情)。查阅supported annotations列表以查看可以在`@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 Support Classes

org.springframework.test.context.testng 包为基于 TestNG 的测试用例提供了以下支持类:

  • AbstractTestNGSpringContextTests

  • AbstractTransactionalTestNGSpringContextTests

AbstractTestNGSpringContextTests 是一个抽象基础测试类,它在 TestNG 环境中集成了 Spring 测试上下文框架和显式的 ApplicationContext 测试支持。当扩展 AbstractTestNGSpringContextTests 时,可以访问 protected applicationContext 实例变量,可以用它执行显式 bean 查询或测试整个上下文的 state。

`AbstractTransactionalTestNGSpringContextTests`是`AbstractTestNGSpringContextTests`的抽象事务扩展,为JDBC访问添加了一些便捷功能。此类期望在`ApplicationContext`中定义`javax.sql.DataSource`bean和`PlatformTransactionManager`bean。在扩展`AbstractTransactionalTestNGSpringContextTests`时,你可以访问`protected``jdbcTemplate`实例变量,可使用它来运行查询数据库的SQL语句。你可以使用此类查询来确认在运行与数据库相关的应用程序代码之前和之后的状态,并且Spring会确保此类查询在与应用程序代码相同的事务范围内运行。在与ORM工具结合使用时,请务必避免使用false positives。如JDBC Testing Support中所述,`AbstractTransactionalTestNGSpringContextTests`还提供了一些便利方法,通过使用上述`jdbcTemplate`来委托给`JdbcTestUtils`中的方法。此外,`AbstractTransactionalTestNGSpringContextTests`提供了一个`executeSqlScript(..)`方法,用于针对配置的`DataSource`运行SQL脚本。

这些类便于扩展。如果您不希望您的测试类绑定到 Spring 特有的类层次结构,则可以使用 @ContextConfiguration@TestExecutionListeners 等手动为测试类添加 TestContextManager 插值,以配置您自己的自定义测试类。请参见 AbstractTestNGSpringContextTests 的源代码,了解如何为测试类添加插值示例。