TestContext 框架支持类

本节介绍支持 Spring TestContext 框架的各种类。

Spring JUnit 4 运行器

Spring TestContext 框架通过自定义运行器(在 JUnit 4.12 或更高版本上支持)提供与 JUnit 4 的完全集成。通过使用 @RunWith(SpringJUnit4ClassRunner.class) 或更短的 @RunWith(SpringRunner.class) 变体注释测试类,开发人员可以实现标准的基于 JUnit 4 的单元和集成测试,同时利用 TestContext 框架的优势,例如支持加载应用程序上下文、测试实例的依赖注入、事务性测试方法执行等等。如果您想将 Spring TestContext 框架与其他运行器(例如 JUnit 4 的 Parameterized 运行器)或第三方运行器(例如 MockitoJUnitRunner)一起使用,您可以选择使用 Spring 对 JUnit 规则的支持

以下代码清单显示了使用自定义 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 规则

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

  • SpringClassRule

  • SpringMethodRule

SpringClassRule 是一个 JUnit TestRule,它支持 Spring TestContext 框架的类级功能,而 SpringMethodRule 是一个 JUnit MethodRule,它支持 Spring TestContext 框架的实例级和方法级功能。

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

为了支持 TestContext 框架的全部功能,您必须将 SpringClassRuleSpringMethodRule 结合使用。以下示例展示了在集成测试中声明这些规则的正确方法

  • 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 是一个抽象基测试类,它将 Spring TestContext 框架与 JUnit 4 环境中的显式 ApplicationContext 测试支持集成在一起。当您扩展 AbstractJUnit4SpringContextTests 时,您可以访问一个 protected applicationContext 实例变量,您可以使用它来执行显式 bean 查找或测试整个上下文的狀態。

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

这些类是为了扩展方便而提供的。如果您不想将测试类绑定到特定于 Spring 的类层次结构,您可以使用 `@RunWith(SpringRunner.class)` 或 Spring 的 JUnit 规则 配置您自己的自定义测试类。

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` 示例。

以下代码清单展示了如何配置测试类以将 `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创建一个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...
	}
}

有关@SpringJUnitConfig@SpringJUnitWebConfig的文档,请参阅Spring JUnit Jupiter 测试注解以了解更多详细信息。

使用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 将OrderService bean 从TestConfig.class 加载的ApplicationContext 中注入到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 将OrderServiceTestConfig.class 加载的ApplicationContext 中注入到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 Framework 5.0 起,Spring TestContext Framework 就支持在 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 文件全局更改默认模式。有关详细信息,请参阅 "更改默认封闭配置继承模式" 说明。

虽然以下“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 是一个抽象的基测试类,它将 Spring TestContext 框架与 TestNG 环境中的显式 ApplicationContext 测试支持集成在一起。当您扩展 AbstractTestNGSpringContextTests 时,您可以访问一个 protectedapplicationContext 实例变量,您可以使用它来执行显式 bean 查找或测试整个上下文的 state。

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

这些类是扩展的便利方法。如果您不希望您的测试类绑定到特定于 Spring 的类层次结构,您可以使用 @ContextConfiguration@TestExecutionListeners 等配置自己的自定义测试类,并通过使用 TestContextManager 手动为您的测试类添加 instrumentation。有关如何为您的测试类添加 instrumentation 的示例,请参阅 AbstractTestNGSpringContextTests 的源代码。