测试Spring应用可以使用的注解

https://docs.spring.io/spring-framework/reference/testing/annotations.html

本节介绍了在测试Spring应用程序时可以使用的注解。

标准注解支持

Spring TestContext Framework 的所有配置都支持以下具有标准语义的注解。请注意,这些注解并不特定于测试,并且可以在 Spring Framework 中的任何位置使用。

  • @Autowired
  • @Qualifier
  • @Value
  • @Resourcejakarta.annotation)如果JSR-250存在
  • @ManagedBeanjakarta.annotation)如果JSR-250存在
  • @Inject (jakarta.inject)如果JSR-330存在
  • @Named(jakarta.inject)如果JSR-330存在
  • @PersistenceContext (jakarta.persistence) 如果JPA存在
  • @PersistenceUnit(jakarta.persistence) 如果JPA存在
  • @Transactionalorg.springframework.transaction.annotation),仅支持有限属性

在Spring TestContext Framework中,可以在ApplicationContext中配置的任何应用程序组件上使用具有标准语义的@PostConstruct@PreDestroy注解。然而,这些生命周期注解在实际测试类中的使用是有限制的。

如果一个测试类中的方法被@PostConstruct注解,那么这个方法会在底层测试框架的任何前置方法(例如,使用JUnit Jupiter的@BeforeEach注解的方法)之前运行,并且这适用于测试类中的每个测试方法。另一方面,如果一个测试类中的方法被@PreDestroy注解,那么这个方法永远不会运行。因此,在测试类中,建议使用底层测试框架的测试生命周期回调,而不是@PostConstruct@PreDestroy

Spring测试注解

Spring框架提供了一系列Spring特有的注解,你可以与TestContext框架结合使用这些注解进行单元测试和集成测试。

Spring的测试注解包括以下几种:

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

@BootstrapWith

@BootstrapWith 是一个类级别的注解,你可以使用它来配置 Spring TestContext Framework 的引导方式。具体来说,你可以使用 @BootstrapWith 来指定一个自定义的 TestContextBootstrapper

@ContextConfiguration

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

资源位置通常是位于类路径中的 XML 配置文件或 Groovy 脚本,而组件类通常是带有 @Configuration 注解的类。然而,资源位置也可以引用文件系统中的文件和脚本,组件类也可以是带有 @Component 注解的类、带有 @Service 注解的类等。

以下示例展示了引用 XML 文件的 @ContextConfiguration 注解:

@ContextConfiguration("/test-config.xml")
class XmlApplicationContextTests {
	// class body...
}

以下示例展示了引用一个类的 @ContextConfiguration 注解:

@ContextConfiguration(classes = TestConfig.class)
class ConfigClassApplicationContextTests {
	// class body...
}

除了声明资源位置或组件类之外,你还可以使用 @ContextConfiguration 来声明 ApplicationContextInitializer 类。以下示例展示了这种情况:

@ContextConfiguration(initializers = CustomContextInitializer.class)
class ContextInitializerTests {
	// class body...
}

你还可以选择使用 @ContextConfiguration 来声明 ContextLoader 策略。但请注意,你通常不需要显式配置加载器,因为默认加载器支持initializers 以及资源locations 或组件classes

以下示例同时使用了一个位置和一个加载器:

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

@ContextConfiguration 提供支持继承资源位置或配置类,以及由超类或封闭类声明的上下文初始化器。

@WebAppConfiguration

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

以下示例展示了如何使用@WebAppConfiguration注解:

@ContextConfiguration
@WebAppConfiguration
class WebAppTests {
	// class body...
}

要覆盖默认值,可以使用隐式value 属性指定不同的基本资源路径。支持classpath:file:资源前缀。如果没有提供资源前缀,则假定路径是文件系统资源。以下示例展示了如何指定类路径资源:

@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources")
class WebAppTests {
	// class body...
}

请注意,@WebAppConfiguration必须与@ContextConfiguration结合使用,无论是在单个测试类中还是在测试类层次结构中。

@ContextHierarchy

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

@ContextHierarchy({
	@ContextConfiguration("/parent-config.xml"),
	@ContextConfiguration("/child-config.xml")
})
class ContextHierarchyTests {
	// class body...
}
@WebAppConfiguration
@ContextHierarchy({
	@ContextConfiguration(classes = AppConfig.class),
	@ContextConfiguration(classes = WebConfig.class)
})
class WebIntegrationTests {
	// class body...
}

如果需要在测试类层次结构中合并或覆盖上下文层次结构的给定级别的配置,你必须通过在每个相应级别的类层次结构中的@ContextConfigurationname属性提供相同的值来显式命名该级别。

@ContextCustomizerFactories

@ContextCustomizerFactories用于为特定测试类、其子类和嵌套类注册ContextCustomizerFactory实现。

以下示例展示了如何注册两个ContextCustomizerFactory实现:

@ContextConfiguration
@ContextCustomizerFactories({CustomContextCustomizerFactory.class, AnotherContextCustomizerFactory.class})
class CustomContextCustomizerFactoryTests {
	// class body...
}

默认情况下,@ContextCustomizerFactories支持从超类或外部类继承工厂。

@ActiveProfiles

@ActiveProfiles 是一个类级别的注解,用于声明在加载集成测试的 ApplicationContext 时应该激活哪些 bean 定义profile。

以下示例表明dev profile 应该是激活的:

@ContextConfiguration
@ActiveProfiles("dev")
class DeveloperTests {
	// class body...
}

以下示例表明devintegration profile 都应该激活:

@ContextConfiguration
@ActiveProfiles({"dev", "integration"})
class DeveloperIntegrationTests {
	// class body...
}

@ActiveProfiles默认支持从超类或外部类继承激活的bean定义profile。你也可以通过实现自定义的ActiveProfilesResolver并使用@ActiveProfilesresolver属性来注册它,从而以编程方式解析激活的bean定义profile。

@TestPropertySource

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

以下示例展示了如何声明一个类路径下的属性文件:

@ContextConfiguration
@TestPropertySource("/test.properties")
class MyIntegrationTests {
	// class body...
}

以下示例展示了如何声明内联属性:

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

@DynamicPropertySource

@DynamicPropertySource是一个方法级别的注解,你可以使用它来注册动态属性,这些属性将被添加到为集成测试加载的ApplicationContextEnvironment 中的PropertySources集合中。当你事先不知道属性的值时,动态属性非常有用——例如,如果属性由外部资源管理,比如Testcontainers项目管理的容器。

以下示例展示了如何注册一个动态属性:

@ContextConfiguration
class MyIntegrationTests {

	static MyExternalServer server = // ...

	@DynamicPropertySource
	static void dynamicProperties(DynamicPropertyRegistry registry) {
		registry.add("server.port", server::getPort);
	}

	// tests ...
}

@DirtiesContext

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

你可以在同一个类或类层次结构中,将@DirtiesContext同时用作类级别和方法级别的注解。在这种情况下,ApplicationContext将在任何此类注解方法之前或之后以及当前测试类之前或之后被标记为脏,具体取决于配置的methodModeclassMode。当在类级别和方法级别都声明了@DirtiesContext时,将遵循两个注解配置的模式。例如,如果类模式设置为BEFORE_EACH_TEST_METHOD,方法模式设置为AFTER_METHOD,那么上下文将在给定的测试方法之前和之后都被标记为脏。

以下示例解释了在不同的配置场景下,上下文何时会被标记为脏:

  • 在当前的测试类之前,当在一个类上声明时,如果该类的模式设置为BEFORE_CLASS
@DirtiesContext(classMode = BEFORE_CLASS)
class FreshContextTests {
	// some tests that require a new Spring container
}
  • 在当前的测试类之后,当在一个类上声明时,如果该类的模式设置为AFTER_CLASS(即默认的类模式)。
@DirtiesContext
class ContextDirtyingTests {
	// some tests that result in the Spring container being dirtied
}
  • 在当前测试类的每一个测试方法之前,当在一个类上声明时,如果该类的模式设置为BEFORE_EACH_TEST_METHOD
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
class FreshContextTests {
	// some tests that require a new Spring container
}
  • 在当前测试类的每一个测试方法之后,当在一个类上声明时,如果该类的模式设置为AFTER_EACH_TEST_METHOD
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
class ContextDirtyingTests {
	// some tests that result in the Spring container being dirtied
}
  • 在当前测试之前,当在一个方法上声明时,如果该方法的模式设置为BEFORE_METHOD
@DirtiesContext(methodMode = BEFORE_METHOD)
@Test
void testProcessWhichRequiresFreshAppCtx() {
	// some logic that requires a new Spring container
}
  • 在当前测试之后,当在一个方法上声明时,如果该方法的模式设置为AFTER_METHOD(即默认的方法模式)。
@DirtiesContext
@Test
void testProcessWhichDirtiesAppCtx() {
	// some logic that results in the Spring container being dirtied
}

如果你在测试中使用@DirtiesContext,并且其上下文被配置为具有@ContextHierarchy的上下文层次结构的一部分,你可以使用hierarchyMode标志来控制如何清除上下文缓存。默认情况下,会使用一种穷举算法来清除上下文缓存,包括当前级别以及所有其他与当前测试共享公共祖先上下文的上下文层次结构。所有驻留在公共祖先上下文的子层次结构中的ApplicationContext实例都会从上下文缓存中移除并关闭。如果对于特定用例来说,穷举算法做得太过了,你可以指定更简单的当前级别算法,如下所示的例子。

@ContextHierarchy({
	@ContextConfiguration("/parent-config.xml"),
	@ContextConfiguration("/child-config.xml")
})
class BaseTests {
	// class body...
}

class ExtendedTests extends BaseTests {

	@Test
	@DirtiesContext(hierarchyMode = CURRENT_LEVEL)
	void test() {
		// some logic that results in the child context being dirtied
	}
}

@TestExecutionListeners

@TestExecutionListeners 用于为特定的测试类、其子类以及嵌套类注册监听器。如果你希望全局注册监听器,你应该通过自动发现机制来注册它。

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

@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
class CustomTestExecutionListenerTests {
	// class body...
}

默认情况下,@TestExecutionListeners 提供从超类或封装类继承监听器的支持。

@RecordApplicationEvents

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

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

@Commit

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

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

@Commit
@Test
void testProcessWithoutRollback() {
	// ...
}

@Rollback

@Rollback 表示在测试方法完成后,事务测试方法的事务是否应该回滚。如果为 true,则事务会被回滚。否则,事务将被提交。即使在没有显式声明 @Rollback 的情况下,Spring TestContext Framework 中的集成测试的回滚默认也为 true

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

下面的示例会导致测试方法的结果不被回滚(即,结果被提交到数据库):

@Rollback(false)
@Test
void testProcessWithoutRollback() {
	// ...
}

@BeforeTransaction

@BeforeTransaction 表示在事务开始之前,应该运行被该注解标记的 void 方法。这个注解适用于那些已经通过使用 Spring 的 @Transactional 注解配置为在事务中运行的测试方法。@BeforeTransaction 方法不需要是 public 的,也可以在基于 Java 8 的接口默认方法上声明。

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

@BeforeTransaction
void beforeTransaction() {
	// logic to be run before a transaction is started
}

@AfterTransaction

@AfterTransaction 表示标注的 void 方法应该在事务结束后运行,这些测试方法通过使用 Spring 的 @Transactional 注解被配置为在事务中运行。@AfterTransaction 方法不要求必须是公共的,并且可以在基于 Java 8 的接口默认方法上声明。

@AfterTransaction
void afterTransaction() {
	// logic to be run after a transaction has ended
}

@Sql

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

@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
void userTest() {
	// run code that relies on the test schema and test data
}

@SqlConfig

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

@Test
@Sql(
	scripts = "/test-user-data.sql",
	config = @SqlConfig(commentPrefix = "`", separator = "@@")
)
void userTest() {
	// run code that relies on the test data
}

@SqlMergeMode

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

请注意,方法级别的 @SqlMergeMode 声明会覆盖类级别的声明。

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

@SpringJUnitConfig(TestConfig.class)
@Sql("/test-schema.sql")
@SqlMergeMode(MERGE)
class UserTests {

	@Test
	@Sql("/user-test-data-001.sql")
	void standardUserProfile() {
		// run code that relies on test data set 001
	}
}

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

@SpringJUnitConfig(TestConfig.class)
@Sql("/test-schema.sql")
class UserTests {

	@Test
	@Sql("/user-test-data-001.sql")
	@SqlMergeMode(MERGE)
	void standardUserProfile() {
		// run code that relies on test data set 001
	}
}

@SqlGroup

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

@Test
@SqlGroup({
	@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
	@Sql("/test-user-data.sql")
})
void userTest() {
	// run code that uses the test schema and test data
}

@DisabledInAotMode

@DisabledInAotMode 表示在 Spring AOT(提前编译)模式下,被注解的测试类是禁用的,这意味着在构建时不会针对测试类的 ApplicationContext 进行 AOT 优化。

如果一个测试类被 @DisabledInAotMode 注解,所有其他指定加载同一 ApplicationContext 的测试类也必须用 @DisabledInAotMode 进行注解。如果没有对所有这样的测试类进行注解,将在构建时或运行时抛出异常。

当与基于 JUnit Jupiter 的测试一起使用时,@DisabledInAotMode 还表示在 Spring AOT 模式下运行测试套件时,被注解的测试类或测试方法是禁用的。当应用于类级别时,该类中的所有测试方法都将被禁用。在这个意义上,@DisabledInAotMode 的语义与 JUnit Jupiter 的 @DisabledInNativeImage 注解相似。

Spring JUnit 4 测试注解

以下注解仅在使用 SpringRunner,Spring 的 JUnit 4 规则,或 Spring 的 JUnit 4 支持类时受支持:

  • @IfProfileValue
  • @ProfileValueSourceConfiguration
  • @Timed
  • @Repeat

@IfProfileValue

@IfProfileValue 表示针对特定测试环境启用被注解的测试。如果配置的 ProfileValueSource 为提供的 name 返回匹配的value ,则启用该测试。否则,禁用该测试,并实际上忽略它。

可以在类级别、方法级别或两者同时使用 @IfProfileValue。类级别的 @IfProfileValue 使用优先于该类或其子类中任何方法的方法级别使用。具体来说,如果一个测试在类级别和方法级别都被启用,则该测试被启用。缺少 @IfProfileValue 意味着测试被隐式启用。这与 JUnit 4 的 @Ignore 注解的语义类似,不同的是 @Ignore 的存在总是禁用测试。

下面的例子展示了一个带有 @IfProfileValue 注解的测试:

@IfProfileValue(name="java.vendor", value="Oracle Corporation")
@Test
public void testProcessWhichRunsOnlyOnOracleJvm() {
	// some logic that should run only on Java VMs from Oracle Corporation
}

或者,你可以将 @IfProfileValue 配置为一个值列表(具有 OR 语义),以在 JUnit 4 环境中实现类似 TestNG 的测试组支持。考虑以下示例:

@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"})
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
	// some logic that should run only for unit and integration test groups
}

为单元测试和集成测试运行此测试。

@ProfileValueSourceConfiguration

@ProfileValueSourceConfiguration 是一个类级别注解,用于指定在检索通过 @IfProfileValue 注解配置的profile 值时使用哪种类型的 ProfileValueSource。如果测试中没有声明 @ProfileValueSourceConfiguration,则默认使用 SystemProfileValueSource。下面的例子展示了如何使用 @ProfileValueSourceConfiguration

@ProfileValueSourceConfiguration(CustomProfileValueSource.class)
public class CustomProfileValueSourceTests {
	// class body...
}

@Timed

@Timed 表示被注解的测试方法必须在指定的时间段(以毫秒为单位)内完成执行。如果测试执行时间超过了指定的时间段,测试将失败。

时间段包括运行测试方法本身、测试的任何重复,以及任何设置或拆除测试夹具的时间。下面的例子展示了如何使用它:

@Timed(millis = 1000)
public void testProcessWithOneSecondTimeout() {
	// some logic that should not take longer than 1 second to run
}

Spring 的 @Timed 注解与 JUnit 4 的 @Test(timeout=…​) 支持具有不同的语义。具体来说,由于 JUnit 4 处理测试执行超时的方式(即在单独的线程中执行测试方法),@Test(timeout=…​) 如果测试花费时间过长,会预先使测试失败。另一方面,Spring 的 @Timed 不会预先使测试失败,而是在测试完成后才判定失败。

@Repeat

@Repeat 表示被注解的测试方法必须重复运行。测试方法要运行的次数在注解中指定。

要重复的执行范围包括测试方法本身的执行以及任何设置或拆除测试夹具。当与 SpringMethodRule 一起使用时,范围还包括由 TestExecutionListener 实现对测试实例的准备。下面的例子展示了如何使用 @Repeat 注解:

@Repeat(10)
@Test
public void testProcessRepeatedly() {
	// ...
}

Spring JUnit Jupiter 测试注解

以下注解在使用 SpringExtension 和 JUnit Jupiter(即 JUnit 5 中的编程模型)时受支持:

  • @SpringJUnitConfig
  • @SpringJUnitWebConfig
  • @TestConstructor
  • @NestedTestConfiguration
  • @EnabledIf
  • @DisabledIf
  • @DisabledInAotMode

@SpringJUnitConfig

@SpringJUnitConfig 是一个组合注解,它将 JUnit Jupiter 的 @ExtendWith(SpringExtension.class) 与 Spring TestContext Framework 的 @ContextConfiguration 结合在一起。它可以在类级别用作 @ContextConfiguration 的直接替代品。就配置选项而言,@ContextConfiguration@SpringJUnitConfig 之间唯一的区别是,组件类可以在 @SpringJUnitConfig 中用 value 属性声明。

下面的例子展示了如何使用 @SpringJUnitConfig 注解指定一个配置类:

@SpringJUnitConfig(TestConfig.class)
class ConfigurationClassJUnitJupiterSpringTests {
	// class body...
}

下面的例子展示了如何使用 @SpringJUnitConfig 注解指定配置文件的位置:

@SpringJUnitConfig(locations = "/test-config.xml")
class XmlJUnitJupiterSpringTests {
	// class body...
}

@SpringJUnitWebConfig

@SpringJUnitWebConfig 是一个组合注解,它将 JUnit Jupiter 的 @ExtendWith(SpringExtension.class) 与 Spring TestContext Framework 的 @ContextConfiguration@WebAppConfiguration 结合在一起。你可以将其用在类级别,作为 @ContextConfiguration@WebAppConfiguration 的直接替代品。就配置选项而言,@ContextConfiguration@SpringJUnitWebConfig 之间唯一的区别是,你可以通过使用 @SpringJUnitWebConfig 中的 value 属性声明组件类。此外,你只能通过使用 @SpringJUnitWebConfig 中的 resourcePath 属性来覆盖 @WebAppConfigurationvalue 属性。

下面的例子展示了如何使用 @SpringJUnitWebConfig 注解指定一个配置类:

@SpringJUnitWebConfig(TestConfig.class)
class ConfigurationClassJUnitJupiterSpringWebTests {
	// class body...
}

下面的例子展示了如何使用 @SpringJUnitWebConfig 注解指定配置文件的位置:

@SpringJUnitWebConfig(locations = "/test-config.xml")
class XmlJUnitJupiterSpringWebTests {
	// class body...
}

@TestConstructor

@TestConstructor是一个类型级别的注解,用于配置测试类构造函数的参数如何从测试的ApplicationContext中的组件进行自动装配。

如果@TestConstructor没有出现在测试类上,或者在元数据中出现,那么将使用默认的测试构造函数自动装配模式。如果构造函数上有局部声明的@Autowired@jakarta.inject.Inject@javax.inject.Inject注解,那么这些注解将优先于@TestConstructor和默认模式。

默认的测试构造函数自动装配模式可以通过将JVM系统属性spring.test.constructor.autowire.mode设置为all来更改。或者,也可以通过SpringProperties机制设置默认模式。

自Spring框架5.3起,默认模式也可以配置为JUnit平台的配置参数。

如果没有设置spring.test.constructor.autowire.mode属性,测试类构造函数将不会被自动装配。

自Spring框架5.2起,@TestConstructor仅与JUnit Jupiter的SpringExtension一起支持使用。请注意,SpringExtension通常会自动注册——例如,当使用@SpringJUnitConfig@SpringJUnitWebConfig注解或者来自Spring Boot Test的各种测试相关注解时。

@NestedTestConfiguration

@NestedTestConfiguration是一个类型级别的注解,用于配置在内部测试类的封闭类层次结构中如何处理Spring测试配置注解。

如果测试类、其超类型层次结构或其封闭类层次结构中没有出现@NestedTestConfiguration,或者在元数据中出现,那么将使用默认的封闭配置继承模式。

默认的封闭配置继承模式是INHERIT,但可以通过将JVM系统属性spring.test.enclosing.configuration设置为OVERRIDE来更改。或者,也可以通过SpringProperties机制设置默认模式。

Spring TestContext框架遵循以下注解的@NestedTestConfiguration语义。

  • @BootstrapWith
  • @ContextConfiguration
  • @WebAppConfiguration
  • @ContextHierarchy
  • @ContextCustomizerFactories
  • @ActiveProfiles
  • @TestPropertySource
  • @DynamicPropertySource
  • @DirtiesContext
  • @TestExecutionListeners
  • @RecordApplicationEvents
  • @Transactional
  • @Commit
  • @Rollback
  • @Sql
  • @SqlConfig
  • @SqlMergeMode
  • @TestConstructor

@NestedTestConfiguration的使用通常只在与JUnit Jupiter中的@Nested测试类结合时才有意义;然而,可能还有其他支持Spring和嵌套测试类的测试框架使用这个注解。

@EnabledIf

@EnabledIf用于表示,如果提供的表达式计算结果为true,则标记的JUnit Jupiter测试类或测试方法将被启用并应该运行。具体来说,如果表达式计算结果为Boolean.TRUE或等于true(忽略大小写)的字符串,则测试被启用。当应用于类级别时,该类中的所有测试方法默认也会自动启用。

表达式可以是以下任意一种:

  • Spring Expression Language(SpEL)表达式。例如:@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
  • Spring环境中可用的属性的占位符。例如:@EnabledIf("${smoke.tests.enabled}")
  • 文本字面量。例如:@EnabledIf("true")

然而,如果不是动态解析属性占位符的结果,文本字面量的实际应用价值为零,因为@EnabledIf("false")等同于@Disabled,而@EnabledIf("true")在逻辑上是没有意义的。

你可以将@EnabledIf作为元注解来创建自定义的组合注解。例如,你可以按如下方式创建一个自定义的@EnabledOnMac注解:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@EnabledIf(
	expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
	reason = "Enabled on Mac OS"
)
public @interface EnabledOnMac {}

@EnabledOnMac仅作为可能的示例。如果你确实有这个用例,请使用JUnit Jupiter内置的@EnabledOnOs(MAC)支持。

自JUnit 5.7起,JUnit Jupiter也有一个名为@EnabledIf的条件注解。因此,如果你希望使用Spring的@EnabledIf支持,请确保你从正确的包中导入了注解类型。

@DisabledIf

@DisabledIf用于表示,如果提供的表达式计算结果为true,则标记的JUnit Jupiter测试类或测试方法将被禁用且不应运行。具体来说,如果表达式计算结果为Boolean.TRUE或等于true(忽略大小写)的字符串,则测试被禁用。当应用于类级别时,该类中的所有测试方法默认也会自动禁用。

表达式可以是以下任意一种:

  • Spring Expression Language(SpEL)表达式。例如:@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
  • Spring环境中可用的属性的占位符。例如:@DisabledIf("${smoke.tests.disabled}")
  • 文本字面量。例如:@DisabledIf("true")

然而,如果不是动态解析属性占位符的结果,文本字面量的实际应用价值为零,因为@DisabledIf("true")等同于@Disabled,而@DisabledIf("false")在逻辑上是没有意义的。

你可以将@DisabledIf作为元注解来创建自定义的组合注解。例如,你可以按如下方式创建一个自定义的@DisabledOnMac注解:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(
	expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
	reason = "Disabled on Mac OS"
)
public @interface DisabledOnMac {}

@DisabledOnMac仅作为可能的示例。如果你确实有这个用例,请使用JUnit Jupiter内置的@DisabledOnOs(MAC)支持。

自JUnit 5.7起,JUnit Jupiter也有一个名为@DisabledIf的条件注解。因此,如果你希望使用Spring的@DisabledIf支持,请确保你从正确的包中导入了注解类型。

测试的元注解支持

你可以将大多数与测试相关的注解作为元注解来创建自定义的组合注解,并减少测试套件中的配置重复。

你可以将以下每个注解与TestContext框架结合使用作为元注解。

  • @BootstrapWith
  • @ContextConfiguration
  • @ContextHierarchy
  • @ContextCustomizerFactories
  • @ActiveProfiles
  • @TestPropertySource
  • @DirtiesContext
  • @WebAppConfiguration
  • @TestExecutionListeners
  • @Transactional
  • @BeforeTransaction
  • @AfterTransaction
  • @Commit
  • @Rollback
  • @Sql
  • @SqlConfig
  • @SqlMergeMode
  • @SqlGroup
  • @Repeat(仅支持JUnit 4)
  • @Timed(仅支持JUnit 4)
  • @IfProfileValue(仅支持JUnit 4)
  • @ProfileValueSourceConfiguration(仅支持JUnit 4)
  • @SpringJUnitConfig(仅支持JUnit Jupiter)
  • @SpringJUnitWebConfig(仅支持JUnit Jupiter)
  • @TestConstructor(仅支持JUnit Jupiter)
  • @NestedTestConfiguration(仅支持JUnit Jupiter)
  • @EnabledIf(仅支持JUnit Jupiter)
  • @DisabledIf(仅支持JUnit Jupiter)

考虑以下示例:

@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }

@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }

如果我们发现自己在基于JUnit 4的测试套件中重复前一个配置,我们可以通过引入一个自定义的组合注解来减少重复,该注解集中了Spring的公共测试配置,如下所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }

然后我们可以使用自定义的@TransactionalDevTestConfig注解来简化基于JUnit 4的单个测试类的配置,如下所示:

@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class OrderRepositoryTests { }

@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class UserRepositoryTests { }

如果我们编写使用JUnit Jupiter的测试,我们可以进一步减少代码重复,因为JUnit 5中的注解也可以作为元注解使用。考虑以下示例:

@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }

@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }

如果我们发现自己在基于JUnit Jupiter的测试套件中重复前一个配置,我们可以通过引入一个自定义的组合注解来减少重复,该注解集中了Spring和JUnit Jupiter的公共测试配置,如下所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }

然后我们可以使用自定义的@TransactionalDevTestConfig注解来简化基于JUnit Jupiter的单个测试类的配置,如下所示:

@TransactionalDevTestConfig
class OrderRepositoryTests { }

@TransactionalDevTestConfig
class UserRepositoryTests { }

由于JUnit Jupiter支持将@Test@RepeatedTestParameterizedTest等作为元注解使用,你也可以在测试方法级别创建自定义的组合注解。例如,如果我们希望创建一个组合注解,将JUnit Jupiter的@Test@Tag注解与Spring的@Transactional注解结合起来,我们可以创建一个@TransactionalIntegrationTest注解,如下所示:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
public @interface TransactionalIntegrationTest { }

然后我们可以使用自定义的@TransactionalIntegrationTest注解来简化基于JUnit Jupiter的单个测试方法的配置,如下所示:

@TransactionalIntegrationTest
void saveOrder() { }

@TransactionalIntegrationTest
void deleteOrder() { }
  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值