Большинство аннотаций, связанных с тестированием, можно использовать в качестве мета-аннотаций для создания собственных аннотаций и сокращения конфигурационного дублирования в тестовом комплекте.

Можно использовать каждую из следующих аннотаций в качестве мета-аннотации в сочетании с фреймворком TestContext.

  • @BootstrapWith

  • @ContextConfiguration

  • @ContextHierarchy

  • @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)

Рассмотрим следующий пример:

Java
@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 { }
Kotlin
@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 { }

Если обнаружится, что предыдущая конфигурация повторяется в нашем тестовом комплекте на базе JUnit 4, то можно сократить дублирование, введя специальную составную аннотацию, которая централизует общую тестовую конфигурацию для Spring, следующим образом:

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

Затем можно использовать нашу специальную аннотацию @TransactionalDevTestConfig для упрощения конфигурации отдельных тестовых классов на основе JUnit 4, как показано ниже:

Java
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class UserRepositoryTests { }
Kotlin
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class OrderRepositoryTests
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class UserRepositoryTests

Если мы пишем тесты, использующие JUnit Jupiter, то можно еще больше сократить дублирование кода, поскольку аннотации в JUnit 5 могут также использоваться в качестве мета-аннотаций. Рассмотрим следующий пример:

Java
@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 { }
Kotlin
@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, следующим образом:

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

Затем можно использовать нашу специальную аннотацию @TransactionalDevTestConfig для упрощения конфигурации отдельных тестовых классов на основе JUnit Jupite, как показано ниже:

Java
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
Kotlin
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }

Поскольку JUnit Jupiter поддерживает использование @Test, @RepeatedTest, ParameterizedTest и других в качестве мета-аннотаций, можно также создавать собственные составные аннотации на уровне тестового метода. Например, если нужно создать составную аннотацию, которая объединяет аннотации @Test и @Tag из JUnit Jupiter с аннотацией @Transactional из Spring, можно создать аннотацию @TransactionalIntegrationTest следующим образом:

Java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
public @interface TransactionalIntegrationTest { }
Kotlin
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
annotation class TransactionalIntegrationTest { }

Затем можно использовать нашу специальную аннотацию @TransactionalIntegrationTest для упрощения конфигурации отдельных тестовых методов на основе JUnit Jupiter следующим образом:

Java
@TransactionalIntegrationTest
void saveOrder() { }
@TransactionalIntegrationTest
void deleteOrder() { }
Kotlin
@TransactionalIntegrationTest
fun saveOrder() { }
@TransactionalIntegrationTest
fun deleteOrder() { }

Более подробную информацию можно найти на вики-странице Spring Annotation Programming Model.