跳到主要内容

TestContext 框架支持类

ChatGPT-4o 中英对照 TestContext Framework Support Classes

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

Spring JUnit 4 Runner

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

以下代码清单展示了配置一个测试类以使用自定义 Spring Runner 运行的最低要求:

@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {

@Test
public void testMethod() {
// test logic...
}
}
java

在前面的示例中,@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 结合使用。以下示例展示了在集成测试中声明这些规则的正确方法:

// 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...
}
}
java

JUnit 4 支持类

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

  • AbstractJUnit4SpringContextTests

  • AbstractTransactionalJUnit4SpringContextTests

AbstractJUnit4SpringContextTests 是一个抽象基测试类,它在 JUnit 4 环境中将 Spring TestContext 框架与显式的 ApplicationContext 测试支持集成在一起。当你扩展 AbstractJUnit4SpringContextTests 时,你可以访问一个 protectedapplicationContext 实例变量,你可以使用它来执行显式的 bean 查找或测试整个上下文的状态。

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

提示

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

SpringExtension 用于 JUnit Jupiter

Spring TestContext 框架提供了与 JUnit 5 引入的 JUnit Jupiter 测试框架的全面集成。通过在测试类上使用 @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 示例。

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

// 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...
}
}
java

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

下面的示例使用 @SpringJUnitConfig 来减少前一个示例中使用的配置量:

// 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...
}
}
java

类似地,下面的示例使用 @SpringJUnitWebConfig 来创建一个 WebApplicationContext,以便与 JUnit Jupiter 一起使用:

// 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...
}
}
java

有关 @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 交互,这可能会导致错误。

要在与 @TestInstance(PER_CLASS) 结合使用时使用 "before test method" 或 "after test method" 模式的 @DirtiesContext,必须配置从 Spring 提供的依赖项通过字段或 setter 注入,以便它们可以在测试方法调用之间重新注入。

在下面的示例中,Spring 从 TestConfig.class 加载的 ApplicationContext 中注入 OrderService bean 到 OrderServiceIntegrationTests 构造函数中。

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

private final OrderService orderService;

@Autowired
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}

// tests that use the injected OrderService
}
java

注意,此功能允许测试依赖项为 final,因此是不可变的。

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

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

private final OrderService orderService;

OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}

// tests that use the injected OrderService
}
java

方法注入

如果 JUnit Jupiter 测试方法或测试生命周期回调方法中的参数类型是 ApplicationContext(或其子类型),或者用 @Autowired@Qualifier@Value 注解或元注解标注,Spring 会从测试的 ApplicationContext 中注入对应 bean 的值到该特定参数。

在下面的示例中,Spring 将从 TestConfig.class 加载的 ApplicationContext 中的 OrderService 注入到 deleteOrder() 测试方法中:

@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {

@Test
void deleteOrder(@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
}
}
java

由于 JUnit Jupiter 中 ParameterResolver 支持的强大功能,你可以在一个方法中注入多个依赖,不仅可以来自 Spring,还可以来自 JUnit Jupiter 本身或其他第三方扩展。

下面的示例展示了如何让 Spring 和 JUnit Jupiter 同时将依赖注入到 placeOrderRepeatedly() 测试方法中。

@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
}
}
java

注意,使用 JUnit Jupiter 中的 @RepeatedTest 可以让测试方法访问 RepetitionInfo

@Nested 测试类配置

Spring TestContext Framework 支持在 JUnit Jupiter 中的 @Nested 测试类上使用与测试相关的注解,包括对从封闭类继承测试类配置的一流支持,并且默认情况下将继承此类配置。要从默认的 INHERIT 模式更改为 OVERRIDE 模式,可以为单个 @Nested 测试类添加注解 @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)。显式的 @NestedTestConfiguration 声明将适用于被注解的测试类及其任何子类和嵌套类。因此,您可以为顶级测试类添加 @NestedTestConfiguration 注解,这将递归地应用于其所有嵌套测试类。

提示

如果您正在开发一个与 Spring TestContext 框架集成的组件,并且需要支持封闭类层次结构中的注解继承,则必须使用 TestContextAnnotationUtils 中提供的注解搜索工具,以遵循 @NestedTestConfiguration 语义。

为了允许开发团队将默认值更改为 OVERRIDE——例如,为了与 Spring Framework 5.0 到 5.2 兼容——可以通过 JVM 系统属性或类路径根目录中的 spring.properties 文件全局更改默认模式。有关详细信息,请参阅“更改默认封闭配置继承模式”注释。

尽管以下“Hello World”示例非常简单,但它展示了如何在顶级类上声明通用配置,并由其 @Nested 测试类继承。在这个特定示例中,只有 TestConfig 配置类被继承。每个嵌套测试类提供自己的一组活动配置文件,从而为每个嵌套测试类生成一个独特的 ApplicationContext(详情请参阅上下文缓存)。请查阅支持的注解列表,以了解哪些注解可以在 @Nested 测试类中继承。

@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");
}
}
}
java

TestNG 支持类

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

  • AbstractTestNGSpringContextTests

  • AbstractTransactionalTestNGSpringContextTests

AbstractTestNGSpringContextTests 是一个抽象基测试类,它在 TestNG 环境中将 Spring TestContext 框架与显式的 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 的源代码,了解如何增强您的测试类的示例。