对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

测试上下文框架支持类

本节介绍支持 Spring TestContext 框架的各种类。spring-doc.cadn.net.cn

Spring JUnit 4 运行器

Spring TestContext 框架通过自定义运行器(支持 JUnit 4.12 或更高版本)与 JUnit 4 实现了完全集成。通过在测试类上使用 @RunWith(SpringJUnit4ClassRunner.class) 注解,或者更简短的 @RunWith(SpringRunner.class) 变体,开发者可以编写基于标准 JUnit 4 的单元测试和集成测试,同时还能享受 TestContext 框架带来的诸多优势,例如加载应用上下文、对测试实例进行依赖注入、事务化的测试方法执行等。如果你希望在使用其他运行器(例如 JUnit 4 的 Parameterized 运行器)或第三方运行器(例如 MockitoJUnitRunner)的同时使用 Spring TestContext 框架,也可以选择使用 Spring 对 JUnit 规则(JUnit Rules)的支持spring-doc.cadn.net.cn

以下代码清单展示了配置测试类以使用自定义 Spring Runner 运行所需的最低要求:spring-doc.cadn.net.cn

@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 被配置为一个空列表,以禁用默认监听器;否则,默认监听器将要求通过 ApplicationContext 配置一个 @ContextConfigurationspring-doc.cadn.net.cn

Spring JUnit 4 规则

org.springframework.test.context.junit4.rules 包提供了以下 JUnit 4 规则(支持 JUnit 4.12 或更高版本):spring-doc.cadn.net.cn

SpringClassRule 是一个 JUnit TestRule,用于支持 Spring TestContext 框架的类级别功能;而 SpringMethodRule 是一个 JUnit MethodRule,用于支持 Spring TestContext 框架的实例级别和方法级别功能。spring-doc.cadn.net.cn

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

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

// 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 或更高版本):spring-doc.cadn.net.cn

AbstractJUnit4SpringContextTests 是一个抽象基测试类,它将 Spring TestContext 框架与显式的 ApplicationContext 测试支持集成到 JUnit 4 环境中。当你继承 AbstractJUnit4SpringContextTests 时,可以访问一个受保护(protected)的 applicationContext 实例变量,用于执行显式的 Bean 查找,或测试整个上下文的状态。spring-doc.cadn.net.cn

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

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

用于 JUnit Jupiter 的 Spring 扩展

Spring TestContext 框架提供了与 JUnit Jupiter 测试框架的完整集成,后者是在 JUnit 5 中引入的。通过在测试类上使用 @ExtendWith(SpringExtension.class) 注解,您可以编写基于标准 JUnit Jupiter 的单元测试和集成测试,同时还能享受 TestContext 框架带来的诸多优势,例如支持加载应用上下文、对测试实例进行依赖注入、事务化的测试方法执行等。spring-doc.cadn.net.cn

此外,得益于 JUnit Jupiter 中丰富的扩展 API,Spring 提供了以下功能,这些功能超出了 Spring 为 JUnit 4 和 TestNG 所支持的功能集:spring-doc.cadn.net.cn

以下代码清单展示了如何配置一个测试类,以结合使用 SpringExtension@ContextConfigurationspring-doc.cadn.net.cn

// 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 中将注解用作元注解(meta-annotations),Spring 提供了组合注解 @SpringJUnitConfig@SpringJUnitWebConfig,以简化测试 ApplicationContext 和 JUnit Jupiter 的配置。spring-doc.cadn.net.cn

以下示例使用 @SpringJUnitConfig 来减少前一个示例中使用的配置量:spring-doc.cadn.net.cn

// 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 配合使用:spring-doc.cadn.net.cn

// 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 测试注解文档中关于xref page../annotations/integration-junit-jupiter.html的部分。spring-doc.cadn.net.cn

使用依赖注入的SpringExtension

SpringExtension 实现了来自 JUnit Jupiter 的 ParameterResolver 扩展 API,这使得 Spring 能够为测试构造函数、测试方法以及测试生命周期回调方法提供依赖注入。spring-doc.cadn.net.cn

具体来说,SpringExtension 可以从测试的ApplicationContext中注入依赖到使用@BeforeAll@AfterAll@BeforeEach@AfterEach@Test@RepeatedTest@ParameterizedTest等注解修饰的测试构造函数和方法中。spring-doc.cadn.net.cn

构造函数注入

如果 JUnit Jupiter 测试类构造函数中的某个特定参数类型为 ApplicationContext(或其子类型),或者该参数被 @Autowired@Qualifier@Value 注解(或元注解)所标注,Spring 将从测试的 ApplicationContext 中注入相应的 Bean 或值到该参数。spring-doc.cadn.net.cn

如果测试类的构造函数被认为是可自动装配的(autowirable),Spring 还可以配置为自动装配该构造函数的所有参数。当满足以下任一条件时(按优先级顺序),该构造函数就被视为可自动装配的。spring-doc.cadn.net.cn

有关 @TestConstructor 的使用以及如何更改全局测试构造函数自动装配模式的详细信息,请参阅 @TestConstructorspring-doc.cadn.net.cn

如果测试类的构造函数被视为可自动装配(autowirable),Spring 将负责解析该构造函数中所有参数的值。 因此,JUnit Jupiter 中注册的其他任何 ParameterResolver 都无法为此类构造函数解析参数。

如果使用 @TestInstance(PER_CLASS) 在测试方法之前或之后关闭测试的 @DirtiesContext,则测试类的构造函数注入不得与 JUnit Jupiter 的 ApplicationContext 功能结合使用。spring-doc.cadn.net.cn

原因是 @TestInstance(PER_CLASS) 指示 JUnit Jupiter 在测试方法调用之间缓存测试实例。因此,该测试实例将保留对原本从已关闭的 ApplicationContext 中注入的 bean 的引用。由于在此类场景中测试类的构造函数仅会被调用一次,依赖注入不会再次发生,后续的测试将与来自已关闭的 ApplicationContext 的 bean 进行交互,这可能会导致错误。spring-doc.cadn.net.cn

要将 @DirtiesContext 与“测试方法前”或“测试方法后”模式结合使用,并同时使用 @TestInstance(PER_CLASS),必须将 Spring 的依赖项配置为通过字段注入或 setter 注入的方式提供,以便在测试方法调用之间能够重新注入这些依赖。spring-doc.cadn.net.cn

在以下示例中,Spring 将从通过 OrderService 加载的 ApplicationContext 中注入 TestConfig.class bean 到 OrderServiceIntegrationTests 的构造函数中。spring-doc.cadn.net.cn

@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-doc.cadn.net.cn

如果 spring.test.constructor.autowire.mode 属性设置为 all(请参阅 @TestConstructor),我们可以省略前一个示例中构造函数上对 @Autowired 的声明,结果如下所示。spring-doc.cadn.net.cn

@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-doc.cadn.net.cn

在以下示例中,Spring 将从 OrderService 加载的 ApplicationContext 中的 TestConfig.class 注入到 deleteOrder() 测试方法中:spring-doc.cadn.net.cn

@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-doc.cadn.net.cn

以下示例展示了如何让 Spring 和 JUnit Jupiter 同时将依赖项注入到 placeOrderRepeatedly() 测试方法中。spring-doc.cadn.net.cn

@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 注解可以让测试方法访问 RepetitionInfospring-doc.cadn.net.cn

@Nested测试类配置

自 Spring Framework 5.0 起,Spring TestContext 框架已支持在 JUnit Jupiter 的 @Nested 测试类上使用与测试相关的注解;然而,直到 Spring Framework 5.3 为止,类级别的测试配置注解还不能像从父类继承那样从外围类中继承spring-doc.cadn.net.cn

Spring Framework 5.3 引入了对从外部类继承测试类配置的一流支持,并且此类配置默认会被继承。若要将默认的 INHERIT 模式更改为 OVERRIDE 模式,您可以在单个 @Nested 测试类上添加注解 @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)。显式声明的 @NestedTestConfiguration 将应用于被注解的测试类及其所有子类和嵌套类。因此,您可以在顶层测试类上添加 @NestedTestConfiguration 注解,该注解将递归地应用于其所有嵌套测试类。spring-doc.cadn.net.cn

为了允许开发团队将默认值更改为 OVERRIDE——例如, 以兼容 Spring Framework 5.0 至 5.2 版本——可以通过 JVM 系统属性或位于类路径根目录下的 spring.properties 文件全局更改默认模式。详情请参见“更改默认的封闭配置继承模式”说明。spring-doc.cadn.net.cn

尽管下面的“Hello World”示例非常简单,但它展示了如何在顶层类上声明通用配置,并由其 @Nested 测试类继承。在本例中,仅继承了 TestConfig 配置类。每个嵌套测试类都提供自己的一组激活的配置文件(active profiles),从而为每个嵌套测试类生成一个独立的 ApplicationContext(详情请参见上下文缓存)。请查阅支持的注解列表,以了解哪些注解可以在 @Nested 测试类中被继承。spring-doc.cadn.net.cn

@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 的测试用例提供了以下支持类:spring-doc.cadn.net.cn

AbstractTestNGSpringContextTests 是一个抽象基测试类,它将 Spring TestContext 框架与显式的 ApplicationContext 测试支持集成到 TestNG 环境中。当你继承 AbstractTestNGSpringContextTests 时,可以访问一个受保护(protected)的 applicationContext 实例变量,用于执行显式的 Bean 查找,或测试整个上下文的状态。spring-doc.cadn.net.cn

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

这些类是为了方便扩展而提供的。如果你不希望你的测试类绑定到 Spring 特定的类层次结构,你可以通过使用 @ContextConfiguration@TestExecutionListeners 等注解,并手动在你的测试类中使用 TestContextManager 进行装配,来配置你自己的自定义测试类。有关如何装配测试类的示例,请参见 AbstractTestNGSpringContextTests 的源代码。