Most testing-related annotations can be used as meta annotations to create your own annotations and reduce configuration duplication in the test suite.
Each of the following annotations can be used as a meta annotation in combination with the TestContext framework.
-
@BootstrapWith -
@ContextConfiguration -
@ContextHierarchy -
@ActiveProfiles -
@TestPropertySource -
@DirtiesContext -
@WebAppConfiguration -
@TestExecutionListeners -
@Transactional -
@BeforeTransaction -
@AfterTransaction -
@Commit -
@Rollback -
@Sql -
@SqlConfig -
@SqlMergeMode -
@SqlGroup -
@Repeat(only supported on JUnit 4) -
@Timed(only supported on JUnit 4) -
@IfProfileValue(only supported on JUnit 4) -
@ProfileValueSourceConfiguration(only supported on JUnit 4) -
@SpringJUnitConfig(only supported on JUnit Jupiter) -
@SpringJUnitWebConfig(only supported on JUnit Jupiter) -
@TestConstructor(only supported on JUnit Jupiter) -
@NestedTestConfiguration(only supported on JUnit Jupiter) -
@EnabledIf(only supported on JUnit Jupiter) -
@DisabledIf(only supported on JUnit Jupiter)
Consider the following example:
@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 { }
@RunWith(SpringRunner::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@RunWith(SpringRunner::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
If we find that the previous configuration is repeated in our JUnit 4-based test suite, we can reduce the duplication by introducing a special compound annotation that centralizes the overall test configuration for Spring, as follows:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
annotation class TransactionalDevTestConfig { }
You can then use our custom annotation @TransactionalDevTestConfig to simplify the configuration of individual JUnit 4 based test classes, as shown below:
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class UserRepositoryTests { }
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class OrderRepositoryTests
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class UserRepositoryTests
If we write tests using JUnit Jupiter, then we can further reduce code duplication because annotations in JUnit 5 can also be used as meta annotations. Consider the following example:
@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 { }
@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 { }
If we find that the previous configuration is repeated in our JUnit Jupiter-based test suite, we can reduce the duplication by introducing a special compound annotation that centralizes the common test configuration for Spring and JUnit Jupiter, as follows:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
annotation class TransactionalDevTestConfig { }
You can then use our custom annotation @TransactionalDevTestConfig to simplify the configuration of individual test classes based on JUnit Jupite, as shown below:
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
Because JUnit Jupiter supports using @Test, @RepeatedTest, ParameterizedTest and others as meta annotations, you can also create your own compound annotations at the test method level. For example, if you want to create a compound annotation that combines the @Test and @Tag annotations from JUnit Jupiter with the @Transactional annotation from Spring, you can create an annotation @TransactionalIntegrationTest as follows:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
public @interface TransactionalIntegrationTest { }
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
annotation class TransactionalIntegrationTest { }
You can then use our custom annotation @TransactionalIntegrationTest to simplify the configuration of individual test methods based on JUnit Jupiter as follows:
@TransactionalIntegrationTest
void saveOrder() { }
@TransactionalIntegrationTest
void deleteOrder() { }
@TransactionalIntegrationTest
fun saveOrder() { }
@TransactionalIntegrationTest
fun deleteOrder() { }
More information can be found on the wiki page Spring Annotation Programming Model.
GO TO FULL VERSION