Spring 框架提供了以下一组 Spring 特定的注解,你可以在单元和集成测试中与 TestContext 框架一起使用。有关进一步的信息,包括默认属性值、属性别名和其他细节,请参见相应的 javadoc。

Spring 的测试注解包括以下内容:

  • @BootstrapWith
  • @ContextConfiguration
  • @WebAppConfiguration
  • @ContextHierarchy
  • @ActiveProfiles
  • @TestPropertySource
  • @DynamicPropertySource
  • @DirtiesContext
  • @TestExecutionListeners
  • @RecordApplicationEvents
  • @Commit
  • @Rollback
  • @BeforeTransaction
  • @AfterTransaction
  • @Sql
  • @SqlConfig
  • @SqlMergeMode
  • @SqlGroup

@BootstrapWith

@BootstrapWith 是一个类级注解,你可以用它来配置 Spring TestContext 框架的引导方式。具体来说,你可以使用 @BootstrapWith 来指定一个自定义的 TestContextBootstrapper。更多细节请参见 引导 TestContext 框架的部分

@ContextConfiguration

@ContextConfiguration 定义了类级元数据,用于确定如何为集成测试加载和配置 ApplicationContext。具体来说,@ContextConfiguration声明了应用上下文资源位置或用于加载上下文的组件类。

资源位置通常是位于 classpath 中的 XML 配置文件或 Groovy 脚本,而组件类通常是 @Configuration类。然而,资源位置也可以指文件系统中的文件和脚本,而组件类可以是 @Component 类、@Service 类等等。更多细节请参见 组件类

下面的例子显示了一个指向 XML 文件的 @ContextConfiguration注解:

  1. @ContextConfiguration("/test-config.xml") // 应用了一个 xml 文件
  2. class XmlApplicationContextTests {
  3. // class body...
  4. }

下面的例子引用了一个类

  1. @ContextConfiguration(classes = TestConfig.class)
  2. class ConfigClassApplicationContextTests {
  3. // class body...
  4. }

作为声明资源位置或组件类的替代或补充,你可以使用 @ContextConfiguration 来声明 ApplicationContextInitializer 类。下面的例子显示了这样一个情况:

  1. @ContextConfiguration(initializers = CustomContextIntializer.class) // 声明一个初始化类
  2. class ContextInitializerTests {
  3. // class body...
  4. }

你也可以选择使用 @ContextConfiguration来声明 ContextLoader 策略。然而,请注意,你通常不需要明确地配置加载器,因为默认的加载器支持初始化器和资源位置或组件类。

下面的例子同时使用了一个位置和一个加载器:

  1. @ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class)
  2. class CustomLoaderXmlApplicationContextTests {
  3. // class body...
  4. }

:::info @ContextConfiguration 提供了对继承资源位置或配置类以及由超类或包围类声明的上下文初始化器的支持。 :::

更多细节请参见 上下文管理@Nested 测试类配置,以及 @ContextConfiguration javadocs。

@WebAppConfiguration

@WebAppConfiguration 是一个类级注解,你可以用它来声明为集成测试加载的 ApplicationContext 应该是一个 WebApplicationContext。仅仅在测试类上存在 @WebAppConfiguration就可以确保为测试加载 WebApplicationContext,使用默认值 file:src/main/webapp作为 Web 应用程序根的路径(也就是资源基础路径)。资源基础路径在幕后被用来创建一个 MockServletContext,作为测试的 WebApplicationContext 的 ServletContext。

下面的例子展示了如何使用 @WebAppConfiguration注解:

  1. @ContextConfiguration
  2. @WebAppConfiguration
  3. class WebAppTests {
  4. // class body...
  5. }

要覆盖默认值,你可以通过使用隐含值属性指定一个不同的基本资源路径。classpath:file:资源前缀都被支持。如果没有提供资源前缀,路径将被假定为文件系统资源。下面的例子显示了如何指定 classpath 资源:

  1. @ContextConfiguration
  2. @WebAppConfiguration("classpath:test-web-resources")
  3. class WebAppTests {
  4. // class body...
  5. }

请注意,@WebAppConfiguration必须与 @ContextConfiguration一起使用,无论是在一个测试类中还是在一个测试类的层次结构中。更多细节请参见 @WebAppConfiguration javadoc。

@ContextHierarchy

@ContextHierarchy 是一个类级注解,用于为集成测试定义 ApplicationContext 实例的层次结构 @ContextHierarchy 应该用一个或多个@ContextConfiguration 实例的列表来声明,每个实例都定义了上下文层次结构中的一个级别。以下示例演示了 @ContextHierarchy 在单个测试类中的使用(@ContextHierarchy 也可以在测试类层次结构中使用):

  1. @ContextHierarchy({
  2. @ContextConfiguration("/parent-config.xml"),
  3. @ContextConfiguration("/child-config.xml")
  4. })
  5. class ContextHierarchyTests {
  6. // class body...
  7. }
  1. @WebAppConfiguration
  2. @ContextHierarchy({
  3. @ContextConfiguration(classes = AppConfig.class),
  4. @ContextConfiguration(classes = WebConfig.class)
  5. })
  6. class WebIntegrationTests {
  7. // class body...
  8. }

如果你需要在测试类层次结构中合并或覆盖上下文层次结构中的某个级别的配置,你必须明确地命名该级别,在类层次结构中的每个相应级别中为 @ContextConfiguration 中的 name 属性提供相同的值。请参阅 上下文层次结构@ContextHierarchy javadoc 以了解进一步的例子。

@ActiveProfiles

@ActiveProfiles 是一个类级注解,用于声明在为集成测试加载 ApplicationContext 时,哪些 Bean 定义配置文件应该是活动的。

下面的例子表明 dev 配置文件应该是活动的:

  1. @ContextConfiguration
  2. @ActiveProfiles("dev")
  3. class DeveloperTests {
  4. // class body...
  5. }

可以激活多个配置文件

  1. @ContextConfiguration
  2. @ActiveProfiles({"dev", "integration"})
  3. class DeveloperIntegrationTests {
  4. // class body...
  5. }

:::info @ActiveProfiles 默认提供了对继承由超类和包围类声明的活动 bean 定义配置文件的支持。你也可以通过实现一个自定义的ActiveProfilesResolver 并通过使用 @ActiveProfiles 的 resolver 属性来注册它,从而以编程方式解决活动 bean 定义配置文件。 :::

请参阅 Context Configuration with Environment Profiles, @Nested test class configuration, 和 @ActiveProfiles javadoc 以了解例子和进一步细节。

@TestPropertySource

@TestPropertySource 是一个类级注解,你可以用它来配置属性文件和内联属性的位置,以添加到为集成测试加载的 ApplicationContext 的环境中的 PropertySources 集合。

下面的例子演示了如何从 classpath 声明一个属性文件:

  1. @ContextConfiguration
  2. @TestPropertySource("/test.properties")
  3. class MyIntegrationTests {
  4. // class body...
  5. }

下面的例子演示了如何声明内联属性:

  1. @ContextConfiguration
  2. @TestPropertySource(properties = { "timezone = GMT", "port: 4242" })
  3. class MyIntegrationTests {
  4. // class body...
  5. }

关于例子和进一步的细节,请看 用测试属性源的上下文配置

@DynamicPropertySource

@DynamicPropertySource 是一个方法级注解,你可以用它来注册动态属性,将其添加到集成测试加载的 ApplicationContext 的环境中的 PropertySources 集中。当你预先不知道属性的值时,动态属性很有用,例如,如果属性由外部资源管理,如由 Testcontainers 项目管理的容器。

下面的例子演示了如何注册一个动态属性:

  1. @ContextConfiguration
  2. class MyIntegrationTests {
  3. static MyExternalServer server = // ...
  4. @DynamicPropertySource
  5. // 对一个 static 的方法注解
  6. // 接受一个 DynamicPropertyRegistry 作为参数。
  7. static void dynamicProperties(DynamicPropertyRegistry registry) {
  8. // 注册一个动态的 server.port 属性,以便从服务器中懒加载。
  9. registry.add("server.port", server::getPort);
  10. }
  11. // tests ...
  12. }

更多详情请参见《使用动态属性源的上下文配置》。

@DirtiesContext

@DirtiesContext 表示底层的 Spring ApplicationContext 在测试执行过程中被弄脏了(也就是说,测试以某种方式修改或破坏了它,例如,通过改变一个单例 Bean 的状态),应该被关闭。当一个应用程序上下文被标记为脏时,它就会从测试框架的缓存中删除并关闭。因此,对于任何需要具有相同配置元数据的上下文的后续测试,底层的 Spring 容器将被重建。

你可以在同一个类或类的层次结构中使用 @DirtiesContext作为类级和方法级注解。在这种情况下,根据配置的 methodMode 和classMode,ApplicationContext 在任何这种注解的方法之前或之后,以及在当前测试类之前或之后被标记为脏。

下面的例子解释了在各种配置情况下,什么时候上下文会被标记为脏:

  • 在当前测试类之前,当在类模式设置为 BEFORE_CLASS 的类上声明时。

    1. @DirtiesContext(classMode = BEFORE_CLASS)
    2. class FreshContextTests {
    3. // some tests that require a new Spring container
    4. }
  • 在当前测试类之后,当在类模式设置为 AFTER_CLASS(即默认的类模式)的类上声明。

    1. @DirtiesContext
    2. class ContextDirtyingTests {
    3. // some tests that result in the Spring container being dirtied
    4. }
  • 在当前测试类中的每个测试方法之前,当在类模式设置为 BEFORE_EACH_TEST_METHOD 的类上声明时。

    1. @DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
    2. class FreshContextTests {
    3. // some tests that require a new Spring container
    4. }
  • 在当前测试类的每个测试方法之后,当在类模式设置为 AFTER_EACH_TEST_METHOD 的类上声明时。

    1. @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
    2. class ContextDirtyingTests {
    3. // some tests that result in the Spring container being dirtied
    4. }
  • 在当前测试之前,当在一个方法上声明时,方法模式设置为 BEFORE_METHOD。

    1. @DirtiesContext(methodMode = BEFORE_METHOD)
    2. @Test
    3. void testProcessWhichRequiresFreshAppCtx() {
    4. // some logic that requires a new Spring container
    5. }
  • 在当前测试之后,当在一个方法上声明时,方法模式设置为 AFTER_METHOD(即,默认的方法模式)。

    1. @DirtiesContext
    2. @Test
    3. void testProcessWhichDirtiesAppCtx() {
    4. // some logic that results in the Spring container being dirtied
    5. }

如果你在一个测试中使用 @DirtiesContext,其上下文被配置为带有 @ContextHierarchy的上下文层次结构的一部分,你可以使用 hierarchyMode 标志来控制上下文缓存的清除方式。默认情况下,一个详尽的算法被用来清除上下文缓存,不仅包括当前层级,还包括与当前测试共享一个祖先上下文的所有其他上下文层级。所有位于共同祖先上下文的子层次中的 ApplicationContext 实例都会从上下文缓存中删除并关闭。如果穷举算法对于一个特定的用例来说是多余的,你可以指定更简单的当前级别算法,正如下面的例子所示:

  1. @ContextHierarchy({
  2. @ContextConfiguration("/parent-config.xml"),
  3. @ContextConfiguration("/child-config.xml")
  4. })
  5. class BaseTests {
  6. // class body...
  7. }
  8. class ExtendedTests extends BaseTests {
  9. @Test
  10. @DirtiesContext(hierarchyMode = CURRENT_LEVEL)
  11. void test() {
  12. // some logic that results in the child context being dirtied
  13. }
  14. }

关于 EXHAUSTIVE 和 CURRENT_LEVEL 算法的进一步细节,请参见 DirtiesContext.HierarchyMode javadoc

@TestExecutionListeners

@TestExecutionListeners 定义了类级元数据,用于配置应向 TestContextManager 注册的 TestExecutionListener 实现。通常,@TestExecutionListeners是与 @ContextConfiguration一起使用的。

下面的例子显示了如何注册两个 TestExecutionListener 实现:

  1. @ContextConfiguration
  2. @TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
  3. class CustomTestExecutionListenerTests {
  4. // class body...
  5. }

默认情况下,@TestExecutionListeners 提供了对从超类或包围类中继承监听器的支持。参见 @Nested test class configuration@TestExecutionListeners javadoc,以获得一个例子和进一步的细节。

@RecordApplicationEvents

@RecordApplicationEvents 是一个类级注解,用于指示 Spring TestContext 框架记录在执行单个测试期间在 ApplicationContext 中发布的所有应用程序事件。

记录的事件可以通过测试中的 ApplicationEvents API 访问。

请参阅 应用程序事件@RecordApplicationEvents javadoc,了解一个例子和进一步的细节。

@Commit

@Commit 表示一个事务性测试方法的事务应该在测试方法完成后被提交。你可以用 @Commit直接替代 @Rollback(false)来更明确地传达代码的意图。与 @Rollback类似,@Commit也可以被声明为类级或方法级注解。

下面的例子展示了如何使用@Commit注解:

  1. @Commit
  2. @Test
  3. void testProcessWithoutRollback() {
  4. // ...
  5. }

@Rollback

@Rollback 表示在测试方法完成后,事务性测试方法的事务是否应该被回滚。如果为 true,事务将被回滚。否则,事务被提交(参见@Commit )。即使 @Rollback没有明确声明,Spring TestContext 框架中集成测试的回滚也默认为 true。

当声明为类级注解时,@Rollback定义了测试类层次结构中所有测试方法的默认回滚语义。当声明为方法级注解时,@Rollback为特定的测试方法定义了回滚语义,可能会覆盖类级的 @Rollback@Commit语义。

下面的例子导致一个测试方法的结果不被回滚(也就是说,结果被提交到数据库):

  1. @Rollback(false)
  2. @Test
  3. void testProcessWithoutRollback() {
  4. // ...
  5. }

@BeforeTransaction

@BeforeTransaction 表明,对于那些通过使用 Spring 的 @Transactional注解而被配置为在事务中运行的测试方法,注解的无效方法应该在事务开始之前运行。@BeforeTransaction方法不需要是公共的,可以在基于 Java 8 的接口默认方法上声明。

下面的例子展示了如何使用 @BeforeTransaction 注解:

  1. @BeforeTransaction
  2. void beforeTransaction() {
  3. // 在事务开始前要运行的逻辑
  4. }

@AfterTransaction

@AfterTransaction 表明,对于那些通过使用 Spring 的 @Transactional 注解而被配置为在事务中运行的测试方法,注解的无效方法应在事务结束后运行。@AfterTransaction方法不需要是公开的,可以在基于 Java 8 的接口默认方法上声明。

  1. @AfterTransaction
  2. void afterTransaction() {
  3. // logic to be run after a transaction has ended
  4. }

@Sql

@Sql 用于注解测试类或测试方法,以配置在集成测试期间针对给定数据库运行的 SQL 脚本。下面的例子显示了如何使用它:

  1. @Test
  2. @Sql({"/test-schema.sql", "/test-user-data.sql"})
  3. void userTest() {
  4. // 运行依赖于测试模式和测试数据的代码
  5. }

更多细节请参见 用 @Sql 声明式地执行 SQL 脚本

@SqlConfig

@SqlConfig定义了元数据,用于确定如何解析和运行用 @Sql注解配置的 SQL 脚本。下面的例子展示了如何使用它:

  1. @Test
  2. @Sql(
  3. scripts = "/test-user-data.sql",
  4. config = @SqlConfig(commentPrefix = "`", separator = "@@")
  5. // 在SQL脚本中设置注释前缀和分隔符。
  6. )
  7. void userTest() {
  8. // run code that relies on the test data
  9. }

@SqlMergeMode

@SqlMergeMode用于注解测试类或测试方法,以配置方法级 @Sql声明是否与类级 @Sql声明合并。如果 @SqlMergeMode没有在测试类或测试方法上声明,默认情况下将使用 OVERRIDE 合并模式。在 OVERRIDE 模式下,方法级的 @Sql声明将有效覆盖类级的 @Sql声明。

请注意,方法级的 @Sql合并模式声明会覆盖类级的声明。

下面的例子展示了如何在类级使用 @SqlMergeMode:

  1. @SpringJUnitConfig(TestConfig.class)
  2. @Sql("/test-schema.sql")
  3. @SqlMergeMode(MERGE)
  4. class UserTests {
  5. @Test
  6. @Sql("/user-test-data-001.sql")
  7. void standardUserProfile() {
  8. // run code that relies on test data set 001
  9. }
  10. }

为类中的所有测试方法设置 @Sql合并模式为 MERGE。

下面的例子展示了如何在方法层面使用 @SqlMergeMode

  1. @SpringJUnitConfig(TestConfig.class)
  2. @Sql("/test-schema.sql")
  3. class UserTests {
  4. @Test
  5. @Sql("/user-test-data-001.sql")
  6. @SqlMergeMode(MERGE)
  7. void standardUserProfile() {
  8. // run code that relies on test data set 001
  9. }
  10. }

@SqlGroup

@SqlGroup是一个容器注解,它聚合了几个 @Sql注解。你可以原生地使用 @SqlGroup来声明几个嵌套的 @Sql注解,或者你可以结合 Java 8 对可重复注解的支持来使用它,在同一个类或方法上可以多次声明 @Sql,隐含地生成这个容器注解。下面的例子显示了如何声明一个 SQL 组:

  1. @Test
  2. // 声明一组 SQL 脚本。
  3. @SqlGroup({
  4. @Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
  5. @Sql("/test-user-data.sql")
  6. )}
  7. void userTest() {
  8. // run code that uses the test schema and test data
  9. }