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

Можна використовувати кожну з наступних анотацій як мета-анотацію у поєднанні з фреймворком 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.