TestContext框架支持类

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

Spring JUnit 4 运行器

Spring TestContext框架通过自定义运行器与JUnit 4完全集成(支持JUnit 4.12或更高版本)。通过使用@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是一个抽象基测试类,它在JUnit 4环境中将Spring TestContext框架与显式的ApplicationContext测试支持集成在一起。当您扩展AbstractJUnit4SpringContextTests时,您可以访问一个受保护的applicationContext实例变量,您可以使用它来执行显式的bean查找或测试上下文的整体状态。

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

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

用于JUnit Jupiter的SpringExtension

Spring TestContext框架与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创建一个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测试类上使用与测试相关的注解,包括对从封闭类继承测试类配置的一流支持,并且此类配置将默认继承。要将默认的INHERIT模式更改为OVERRIDE模式,可以使用@NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)注解单个@Nested测试类。显式的@NestedTestConfiguration声明将应用于已注解的测试类及其任何子类和嵌套类。因此,可以使用@NestedTestConfiguration注解顶级测试类,这将递归地应用于其所有嵌套测试类。

如果您正在开发一个与Spring TestContext框架集成的组件,并且需要支持封闭类层次结构中的注解继承,则必须使用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是一个抽象基测试类,它将Spring TestContext框架与TestNG环境中的显式ApplicationContext测试支持集成在一起。扩展AbstractTestNGSpringContextTests时,可以访问protected applicationContext实例变量,可以使用它执行显式bean查找或测试上下文的整体状态。

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

这些类是扩展的便利工具。如果您不希望测试类与特定于Spring的类层次结构绑定,则可以使用@ContextConfiguration@TestExecutionListeners等配置您自己的自定义测试类,并使用TestContextManager手动检测您的测试类。有关如何检测测试类的示例,请参见AbstractTestNGSpringContextTests的源代码。