测试上下文框架支持类

本节描述了支持Spring TestContext Framework的各种类。

Spring JUnit 4 Runner

Spring TestContext Framework通过自定义Runner(支持JUnit 4.12或更高版本)完全集成了JUnit 4。通过使用@RunWith(SpringJUnit4ClassRunner.class)或更短的@RunWith(SpringRunner.class)变体对测试类进行注解,开发人员可以实现基于标准JUnit 4的单元测试和集成测试,并同时获得TestContext框架的好处,例如支持加载应用程序上下文、测试实例的依赖注入、事务性测试方法执行等。如果您想要将Spring TestContext Framework与其他Runner(例如JUnit 4的Parameterized Runner)或第三方Runner(例如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是一个支持Spring TestContext Framework类级特性的JUnit TestRule,而SpringMethodRule是一个支持实例级和方法级Spring TestContext Framework特性的JUnit MethodRule

SpringRunner相比,Spring基于规则的JUnit支持具有独立于任何org.junit.runner.Runner实现的优势,因此可以与现有的替代Runner(例如JUnit 4的Parameterized)或第三方Runner(例如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 Framework与JUnit 4环境中的显式ApplicationContext测试支持集成的抽象基础测试类。当您扩展AbstractJUnit4SpringContextTests时,可以访问一个protectedapplicationContext实例变量,您可以使用它来执行显式的bean查找或测试上下文的整体状态。

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

这些类是扩展的便利类。如果您不希望您的测试类与Spring特定的类层次结构绑定,可以通过使用@RunWith(SpringRunner.class)Spring的JUnit规则来配置自定义的测试类。

JUnit Jupiter的SpringExtension

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

此外,由于JUnit Jupiter具有丰富的扩展API,Spring提供了以下功能,超越了Spring为JUnit 4和TestNG提供的功能集:

以下代码清单显示了如何配置测试类以在@ContextConfiguration中使用SpringExtension

  • Java

  • Kotlin

// 指示JUnit Jupiter使用Spring支持扩展测试。
@ExtendWith(SpringExtension.class)
// 指示Spring从TestConfig.class加载ApplicationContext
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {

	@Test
	void testMethod() {
		// 测试逻辑...
	}
}
// 指示JUnit Jupiter使用Spring支持扩展测试。
@ExtendWith(SpringExtension::class)
// 指示Spring从TestConfig::class加载ApplicationContext
@ContextConfiguration(classes = [TestConfig::class])
class SimpleTests {

	@Test
	fun testMethod() {
		// 测试逻辑...
	}
}

由于您还可以在JUnit 5中使用注解作为元注解,Spring提供了@SpringJUnitConfig@SpringJUnitWebConfig组合注解,以简化测试ApplicationContext和JUnit Jupiter的配置。

以下示例使用@SpringJUnitConfig来减少先前示例中使用的配置量:

  • Java

  • Kotlin

// 指示Spring向JUnit Jupiter注册SpringExtension并从TestConfig.class加载ApplicationContext
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {

	@Test
	void testMethod() {
		// 测试逻辑...
	}
}
// 指示Spring向JUnit Jupiter注册SpringExtension并从TestConfig.class加载ApplicationContext
@SpringJUnitConfig(TestConfig::class)
class SimpleTests {

	@Test
	fun testMethod() {
		// 测试逻辑...
	}
}

类似地,以下示例使用@SpringJUnitWebConfig创建WebApplicationContext以供JUnit Jupiter使用:

  • Java

  • Kotlin

// 指示Spring向JUnit Jupiter注册SpringExtension并从TestWebConfig.class加载WebApplicationContext
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {

	@Test
	void testMethod() {
		// 测试逻辑...
	}
}
// 指示Spring向JUnit Jupiter注册SpringExtension并从TestWebConfig::class加载WebApplicationContext
@SpringJUnitWebConfig(TestWebConfig::class)
class SimpleWebTests {

	@Test
	fun testMethod() {
		// 测试逻辑...
	}
}

有关@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注解。

  • 测试类上存在或元存在带有autowireMode属性设置为ALL@TestConstructor

  • 默认的测试构造函数自动装配模式已更改为ALL

有关如何使用@TestConstructor以及如何更改全局测试构造函数自动装配模式的详细信息,请参见@TestConstructor

如果测试类的构造函数被认为是可自动装配的,Spring将负责解析构造函数中所有参数的参数。因此,JUnit Jupiter中注册的其他ParameterResolver无法解析此类构造函数的参数。

测试类的构造函数注入不能与JUnit Jupiter的@TestInstance(PER_CLASS)支持一起使用,如果在测试方法之前或之后使用@DirtiesContext关闭测试的ApplicationContext

原因是@TestInstance(PER_CLASS)指示JUnit Jupiter在测试方法调用之间缓存测试实例。因此,测试实例将保留对最初从已关闭的ApplicationContext中注入的bean的引用。由于在这种情况下测试类的构造函数只会被调用一次,因此依赖注入不会再次发生,随后的测试将与已关闭的ApplicationContext中的bean交互,可能会导致错误。

要在与@TestInstance(PER_CLASS)一起使用“before test method”或“after test method”模式的@DirtiesContext中配置依赖项从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;
	}

	// 使用注入的OrderService的测试
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){
	// 使用注入的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;
	}

	// 使用注入的OrderService的测试
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests(val orderService:OrderService) {
	// 使用注入的OrderService的测试
}

Method Injection

If a parameter in a JUnit Jupiter test method or test lifecycle callback method is of type ApplicationContext (or a sub-type thereof) or is annotated or meta-annotated with @Autowired, @Qualifier, or @Value, Spring injects the value for that specific parameter with the corresponding bean from the test’s ApplicationContext.

In the following example, Spring injects the OrderService from the ApplicationContext loaded from TestConfig.class into the deleteOrder() test method:

  • 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
	}
}

Due to the robustness of the ParameterResolver support in JUnit Jupiter, you can also have multiple dependencies injected into a single method, not only from Spring but also from JUnit Jupiter itself or other third-party extensions.

The following example shows how to have both Spring and JUnit Jupiter inject dependencies into the placeOrderRepeatedly() test method simultaneously.

  • 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
	}
}

Note that the use of @RepeatedTest from JUnit Jupiter lets the test method gain access to the RepetitionInfo.

@Nested 测试类配置

自Spring Framework 5.0以来,Spring TestContext Framework已经支持在JUnit Jupiter中的@Nested测试类上使用与测试相关的注解;然而,在Spring Framework 5.3之前,类级别的测试配置注解不会像从超类中继承的那样从封闭类中继承。

Spring Framework 5.3引入了对从封闭类继承测试类配置的一流支持,并且这样的配置将默认继承。要从默认的INHERIT模式更改为OVERRIDE模式,您可以在单个@Nested测试类上注释@NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)。显式的@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查找或测试整个上下文的状态。

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

这些类是为扩展而提供的便利。如果您不希望您的测试类与Spring特定的类层次结构绑定,您可以通过使用@ContextConfiguration@TestExecutionListeners等自定义测试类,并通过手动为您的测试类配备TestContextManager来配置自己的自定义测试类。查看AbstractTestNGSpringContextTests的源代码,了解如何为您的测试类进行配置的示例。