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

Spring JUnit 4 Runner

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

下面的代码列表显示了配置测试类与自定义 Spring Runner 一起运行的最低要求:

  1. @RunWith(SpringRunner.class)
  2. @TestExecutionListeners({})
  3. public class SimpleTest {
  4. @Test
  5. public void testMethod() {
  6. // test logic...
  7. }
  8. }

在前面的例子中,@TestExecutionListeners 被配置为一个空列表,以禁用默认监听器,否则需要通过 @Contextfiguration 配置ApplicationContext。

Spring JUnit 4 Rules

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 的参数化)或第三方运行器(如 MockitoJUnitRunner)相结合。

为了支持 TestContext 框架的全部功能,你必须将 SpringClassRule 与 SpringMethodRule 结合起来。下面的例子显示了在集成测试中声明这些规则的正确方式:

  1. // 可以通过 @RunWith(...) 指定一个非 Spring Runner。
  2. @ContextConfiguration
  3. public class IntegrationTest {
  4. @ClassRule
  5. public static final SpringClassRule springClassRule = new SpringClassRule();
  6. @Rule
  7. public final SpringMethodRule springMethodRule = new SpringMethodRule();
  8. @Test
  9. public void testMethod() {
  10. // test logic...
  11. }
  12. }

JUnit 4 支持类

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

  • AbstractJUnit4SpringContextTests
  • AbstractTransactionalJUnit4SpringContextTests

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

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

:::tips 这些类是扩展的一种便利。如果你不希望你的测试类被束缚在 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 在 JUnit 4 和 TestNG 支持的功能集之外,还提供了以下功能:

  • 测试构造函数、测试方法和测试生命周期回调方法的依赖注入。更多细节请参见 SpringExtension 的依赖注入。

  • 对基于 SpEL 表达式、环境变量、系统属性等的 条件性测试执行 的强大支持。请参阅 Spring JUnit Jupiter 测试注解 中的 @EnabledIf 和@DisabledIf 文档,以了解更多细节和示例。

  • 自定义组成的注解,结合了 Spring 和 JUnit Jupiter 的注解。更多细节请参见 Meta-Annotation Support for Testing 中的@TransactionalDevTestConfig 和 @TransactionalIntegrationTest 例子。

下面的代码列表显示了如何配置一个测试类,使其与 @ContextConfiguration 一起使用 SpringExtension:

  1. // 指示 JUnit Jupiter 用 Spring 支持扩展测试。
  2. @ExtendWith(SpringExtension.class)
  3. // 指示 Spring 从 TestConfig.class 加载一个 ApplicationContext。
  4. @ContextConfiguration(classes = TestConfig.class)
  5. class SimpleTests {
  6. @Test
  7. void testMethod() {
  8. // test logic...
  9. }
  10. }

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

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

  1. // 指示 Spring 向 JUnit 注册 SpringExtension。
  2. // Jupiter 并从 TestConfig.class 加载一个 ApplicationContext。
  3. @SpringJUnitConfig(TestConfig.class)
  4. class SimpleTests {
  5. @Test
  6. void testMethod() {
  7. // test logic...
  8. }
  9. }

类似地,下面的例子使用 @SpringJUnitWebConfig 来创建一个 WebApplicationContext 以用于 JUnit Jupiter:

  1. // 指示 Spring 向 JUnit 注册 SpringExtension。
  2. // Jupiter 并从 TestWebConfig.class 加载一个 WebApplicationContext。
  3. @SpringJUnitWebConfig(TestWebConfig.class)
  4. class SimpleWebTests {
  5. @Test
  6. void testMethod() {
  7. // test logic...
  8. }
  9. }

请参阅 Spring JUnitConfig 和 @SpringJUnitWebConfig 在 Spring JUnit Jupiter Testing Annotations 中的文档以了解更多细节。

SpringExtension 的依赖注入

SpringExtension 实现了 JUnit Jupiter 的 ParameterResolver 扩展 API,它让 Spring 为测试构造函数、测试方法和测试生命周期回调方法提供依赖注入。

具体来说,SpringExtension 可以将测试的 ApplicationContext 中的依赖注入到用 @BeforeAll@AfterAll@BeforeEach@AfterEach@Test@RepeatedTest@ParameterizedTest等注解的测试构造器和方法中。

构造函数注入

如果 JUnit Jupiter 测试类的构造函数中的特定参数是 ApplicationContext 类型(或其子类型),或被 @Autowired、@Qualifier 或 @Value 注解或元注解,Spring 会用测试的 ApplicationContext 中的相应 Bean 或值来注入该特定参数的值。

如果一个测试类的构造函数被认为是自动的,那么 Spring 也可以被配置为自动连接该构造函数的所有参数。如果满足以下条件之一(按优先级排序),构造函数就被认为是自动的:

  • 该构造函数被 @Autowired 注解。
  • @TestConstructor 存在或元存在于测试类上,其 autowireMode 属性设置为 ALL。
  • 默认的测试构造函数的自动连接模式已被改为 ALL。

关于 @TestConstructor 的使用以及如何改变全局测试构造器的自动布线模式的细节,请参见 @TestConstructor

:::tips 如果一个测试类的构造函数被认为是自动的,Spring 就会承担起解决构造函数中所有参数的参数的责任。因此,在 JUnit Jupiter 注册的其他 ParameterResolver 都不能为这样的构造器解析参数。 :::

:::tips 如果 @DirtiesContext 被用来在测试方法前后关闭测试的 ApplicationContext,那么测试类的构造函数注入不得与 JUnit Jupiter 的 @TestInstance(PER_CLASS) 支持一起使用。

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

要在 “测试方法前 “或 “测试方法后 “模式下与 @TestInstance(PER_CLASS) 一起使用 @DirtiesContext,必须配置来自 Spring 的依赖关系,通过字段或 setter 注入提供,以便它们可以在测试方法调用之间重新注入。 :::

在下面的例子中,Spring 将 TestConfig.class 加载的 ApplicationContext 中的 OrderService Bean 注入到 OrderServiceIntegrationTests 构造函数中:

  1. @SpringJUnitConfig(TestConfig.class)
  2. class OrderServiceIntegrationTests {
  3. private final OrderService orderService;
  4. @Autowired
  5. OrderServiceIntegrationTests(OrderService orderService) {
  6. this.orderService = orderService;
  7. }
  8. // tests that use the injected OrderService
  9. }

请注意,这一特性使测试依赖关系成为最终的,因此是不可改变的。

如果 spring.test.constructor.autowire.mode 属性为 ALL(见 @TestConstructor),我们可以省略前面例子中对构造函数的 @Autowired 声明,结果如下:

  1. @SpringJUnitConfig(TestConfig.class)
  2. class OrderServiceIntegrationTests {
  3. private final OrderService orderService;
  4. OrderServiceIntegrationTests(OrderService orderService) {
  5. this.orderService = orderService;
  6. }
  7. // tests that use the injected OrderService
  8. }

方法注入

如果 JUnit Jupiter 测试方法或测试生命周期回调方法中的参数是 ApplicationContext 类型(或其子类型),或被 @Autowired、@Qualifier 或 @Value 注解或元注解,Spring 将该特定参数的值注入测试的 ApplicationContext 中的相应 Bean。

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

  1. @SpringJUnitConfig(TestConfig.class)
  2. class OrderServiceIntegrationTests {
  3. @Test
  4. void deleteOrder(@Autowired OrderService orderService) {
  5. // 从测试的 ApplicationContext 中使用 orderService
  6. }
  7. }

由于 JUnit Jupiter 中 ParameterResolver 支持的健壮性,你也可以将多个依赖注入一个方法,不仅来自 Spring,也来自 JUnit Jupiter 本身或其他第三方扩展。

下面的例子展示了如何让 Spring 和 JUnit Jupiter 同时向 placeOrderRepeatedly()测试方法注入依赖:

  1. @SpringJUnitConfig(TestConfig.class)
  2. class OrderServiceIntegrationTests {
  3. @RepeatedTest(10)
  4. void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
  5. @Autowired OrderService orderService) {
  6. // 使用来自测试的 ApplicationContext 的 orderService
  7. // 和来自 JUnit Jupiter 的 repetitionInfo。
  8. }
  9. }

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

@Nested 测试类配置

自 Spring Framework 5.0 以来,Spring TestContext 框架一直支持在 JUnit Jupiter 中的 @Nested 测试类上使用测试相关的注解;然而,直到Spring Framework 5.3,类级测试配置注解并没有像从超类中继承一样从包围类中继承。

Spring Framework 5.3 引入了对从包围类中继承测试类配置的一流支持,而且这种配置将被默认继承。要从默认的继承模式变为超越模式,你可以用 @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)来注解单个 @Nested测试类。一个明确的 @NestedTestConfiguration 声明将适用于被注释的测试类以及它的任何子类和嵌套类。因此,你可以用 @NestedTestConfiguration 注释一个顶层测试类,它将递归地适用于所有的嵌套测试类。

为了让开发团队将默认值改为 OVERRIDE—例如,为了与 Spring Framework 5.0 到 5.2 兼容—可以通过 JVM 系统属性或 classpath 根部的 spring.properties 文件全局地改变默认模式。详见 改变默认的包围配置继承模式 说明。

尽管下面这个 「Hello World」的例子非常简单,但它展示了如何在一个顶层类上声明通用配置,并由其 @Nested 测试类继承。在这个特殊的例子中,只有 TestConfig 配置类被继承了。每个嵌套的测试类提供它自己的活动配置文件集,导致每个嵌套的测试类有一个不同的ApplicationContext(详见 Context Caching)。咨询支持的注解列表,看看哪些注解可以在 @Nested 测试类中继承:

  1. @SpringJUnitConfig(TestConfig.class)
  2. class GreetingServiceTests {
  3. @Nested
  4. @ActiveProfiles("lang_en")
  5. class EnglishGreetings {
  6. @Test
  7. void hello(@Autowired GreetingService service) {
  8. assertThat(service.greetWorld()).isEqualTo("Hello World");
  9. }
  10. }
  11. @Nested
  12. @ActiveProfiles("lang_de")
  13. class GermanGreetings {
  14. @Test
  15. void hello(@Autowired GreetingService service) {
  16. assertThat(service.greetWorld()).isEqualTo("Hallo Welt");
  17. }
  18. }
  19. }

TestNG 支持类

这个没有接触过,不翻译,不看

官方文档