You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
8.0 KiB
309 lines
8.0 KiB
[[integration-testing-annotations-meta]] |
|
= Meta-Annotation Support for Testing |
|
|
|
You can use most test-related annotations as |
|
xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[meta-annotations] to create custom composed |
|
annotations and reduce configuration duplication across a test suite. |
|
|
|
You can use each of the following as a meta-annotation in conjunction with the |
|
xref:testing/testcontext-framework.adoc[TestContext framework]. |
|
|
|
* `@BootstrapWith` |
|
* `@ContextConfiguration` |
|
* `@ContextHierarchy` |
|
* `@ContextCustomizerFactories` |
|
* `@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: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@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:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@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 discover that we are repeating the preceding configuration across our JUnit 4-based |
|
test suite, we can reduce the duplication by introducing a custom composed annotation |
|
that centralizes the common test configuration for Spring, as follows: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@Target(ElementType.TYPE) |
|
@Retention(RetentionPolicy.RUNTIME) |
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"}) |
|
@ActiveProfiles("dev") |
|
@Transactional |
|
public @interface TransactionalDevTestConfig { } |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@Target(AnnotationTarget.TYPE) |
|
@Retention(AnnotationRetention.RUNTIME) |
|
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml") |
|
@ActiveProfiles("dev") |
|
@Transactional |
|
annotation class TransactionalDevTestConfig { } |
|
---- |
|
====== |
|
|
|
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the |
|
configuration of individual JUnit 4 based test classes, as follows: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringRunner.class) |
|
@TransactionalDevTestConfig |
|
public class OrderRepositoryTests { } |
|
|
|
@RunWith(SpringRunner.class) |
|
@TransactionalDevTestConfig |
|
public class UserRepositoryTests { } |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@RunWith(SpringRunner::class) |
|
@TransactionalDevTestConfig |
|
class OrderRepositoryTests |
|
|
|
@RunWith(SpringRunner::class) |
|
@TransactionalDevTestConfig |
|
class UserRepositoryTests |
|
---- |
|
====== |
|
|
|
If we write tests that use JUnit Jupiter, we can reduce code duplication even further, |
|
since annotations in JUnit Jupiter can also be used as meta-annotations. Consider the |
|
following example: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@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:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@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 discover that we are repeating the preceding configuration across our JUnit |
|
Jupiter-based test suite, we can reduce the duplication by introducing a custom composed |
|
annotation that centralizes the common test configuration for Spring and JUnit Jupiter, |
|
as follows: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@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:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@Target(AnnotationTarget.TYPE) |
|
@Retention(AnnotationRetention.RUNTIME) |
|
@ExtendWith(SpringExtension::class) |
|
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml") |
|
@ActiveProfiles("dev") |
|
@Transactional |
|
annotation class TransactionalDevTestConfig { } |
|
---- |
|
====== |
|
|
|
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the |
|
configuration of individual JUnit Jupiter based test classes, as follows: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@TransactionalDevTestConfig |
|
class OrderRepositoryTests { } |
|
|
|
@TransactionalDevTestConfig |
|
class UserRepositoryTests { } |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@TransactionalDevTestConfig |
|
class OrderRepositoryTests { } |
|
|
|
@TransactionalDevTestConfig |
|
class UserRepositoryTests { } |
|
---- |
|
====== |
|
|
|
Since JUnit Jupiter supports the use of `@Test`, `@RepeatedTest`, `ParameterizedTest`, |
|
and others as meta-annotations, you can also create custom composed annotations at the |
|
test method level. For example, if we wish to create a composed annotation that combines |
|
the `@Test` and `@Tag` annotations from JUnit Jupiter with the `@Transactional` |
|
annotation from Spring, we could create an `@TransactionalIntegrationTest` annotation, as |
|
follows: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@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:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@Target(AnnotationTarget.TYPE) |
|
@Retention(AnnotationRetention.RUNTIME) |
|
@Transactional |
|
@Tag("integration-test") // org.junit.jupiter.api.Tag |
|
@Test // org.junit.jupiter.api.Test |
|
annotation class TransactionalIntegrationTest { } |
|
---- |
|
====== |
|
|
|
Then we can use our custom `@TransactionalIntegrationTest` annotation to simplify the |
|
configuration of individual JUnit Jupiter based test methods, as follows: |
|
|
|
[tabs] |
|
====== |
|
Java:: |
|
+ |
|
[source,java,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@TransactionalIntegrationTest |
|
void saveOrder() { } |
|
|
|
@TransactionalIntegrationTest |
|
void deleteOrder() { } |
|
---- |
|
|
|
Kotlin:: |
|
+ |
|
[source,kotlin,indent=0,subs="verbatim,quotes"] |
|
---- |
|
@TransactionalIntegrationTest |
|
fun saveOrder() { } |
|
|
|
@TransactionalIntegrationTest |
|
fun deleteOrder() { } |
|
---- |
|
====== |
|
|
|
For further details, see the |
|
{spring-framework-wiki}/Spring-Annotation-Programming-Model[Spring Annotation Programming Model] |
|
wiki page.
|
|
|