2022-11-19 00:33:40 +08:00
|
|
|
|
[[integration-testing-annotations]]
|
|
|
|
|
= Annotations
|
|
|
|
|
|
|
|
|
|
This section covers annotations that you can use when you test Spring applications.
|
|
|
|
|
It includes the following topics:
|
|
|
|
|
|
|
|
|
|
* <<integration-testing-annotations-standard>>
|
|
|
|
|
* <<integration-testing-annotations-spring>>
|
|
|
|
|
* <<integration-testing-annotations-junit4>>
|
|
|
|
|
* <<integration-testing-annotations-junit-jupiter>>
|
|
|
|
|
* <<integration-testing-annotations-meta>>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-standard]]
|
|
|
|
|
== Standard Annotation Support
|
|
|
|
|
|
|
|
|
|
The following annotations are supported with standard semantics for all configurations of
|
|
|
|
|
the Spring TestContext Framework. Note that these annotations are not specific to tests
|
|
|
|
|
and can be used anywhere in the Spring Framework.
|
|
|
|
|
|
|
|
|
|
* `@Autowired`
|
|
|
|
|
* `@Qualifier`
|
|
|
|
|
* `@Value`
|
|
|
|
|
* `@Resource` (jakarta.annotation) if JSR-250 is present
|
|
|
|
|
* `@ManagedBean` (jakarta.annotation) if JSR-250 is present
|
|
|
|
|
* `@Inject` (jakarta.inject) if JSR-330 is present
|
|
|
|
|
* `@Named` (jakarta.inject) if JSR-330 is present
|
|
|
|
|
* `@PersistenceContext` (jakarta.persistence) if JPA is present
|
|
|
|
|
* `@PersistenceUnit` (jakarta.persistence) if JPA is present
|
|
|
|
|
* `@Transactional` (org.springframework.transaction.annotation)
|
|
|
|
|
_with <<testcontext-tx-attribute-support, limited attribute support>>_
|
|
|
|
|
|
|
|
|
|
.JSR-250 Lifecycle Annotations
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
In the Spring TestContext Framework, you can use `@PostConstruct` and `@PreDestroy` with
|
|
|
|
|
standard semantics on any application components configured in the `ApplicationContext`.
|
|
|
|
|
However, these lifecycle annotations have limited usage within an actual test class.
|
|
|
|
|
|
|
|
|
|
If a method within a test class is annotated with `@PostConstruct`, that method runs
|
|
|
|
|
before any before methods of the underlying test framework (for example, methods
|
|
|
|
|
annotated with JUnit Jupiter's `@BeforeEach`), and that applies for every test method in
|
|
|
|
|
the test class. On the other hand, if a method within a test class is annotated with
|
|
|
|
|
`@PreDestroy`, that method never runs. Therefore, within a test class, we recommend that
|
|
|
|
|
you use test lifecycle callbacks from the underlying test framework instead of
|
|
|
|
|
`@PostConstruct` and `@PreDestroy`.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-spring]]
|
|
|
|
|
== Spring Testing Annotations
|
|
|
|
|
|
|
|
|
|
The Spring Framework provides the following set of Spring-specific annotations that you
|
|
|
|
|
can use in your unit and integration tests in conjunction with the TestContext framework.
|
|
|
|
|
See the corresponding javadoc for further information, including default attribute
|
|
|
|
|
values, attribute aliases, and other details.
|
|
|
|
|
|
|
|
|
|
Spring's testing annotations include the following:
|
|
|
|
|
|
|
|
|
|
* <<spring-testing-annotation-bootstrapwith>>
|
|
|
|
|
* <<spring-testing-annotation-contextconfiguration>>
|
|
|
|
|
* <<spring-testing-annotation-webappconfiguration>>
|
|
|
|
|
* <<spring-testing-annotation-contexthierarchy>>
|
|
|
|
|
* <<spring-testing-annotation-activeprofiles>>
|
|
|
|
|
* <<spring-testing-annotation-testpropertysource>>
|
|
|
|
|
* <<spring-testing-annotation-dynamicpropertysource>>
|
|
|
|
|
* <<spring-testing-annotation-dirtiescontext>>
|
|
|
|
|
* <<spring-testing-annotation-testexecutionlisteners>>
|
|
|
|
|
* <<spring-testing-annotation-recordapplicationevents>>
|
|
|
|
|
* <<spring-testing-annotation-commit>>
|
|
|
|
|
* <<spring-testing-annotation-rollback>>
|
|
|
|
|
* <<spring-testing-annotation-beforetransaction>>
|
|
|
|
|
* <<spring-testing-annotation-aftertransaction>>
|
|
|
|
|
* <<spring-testing-annotation-sql>>
|
|
|
|
|
* <<spring-testing-annotation-sqlconfig>>
|
|
|
|
|
* <<spring-testing-annotation-sqlmergemode>>
|
|
|
|
|
* <<spring-testing-annotation-sqlgroup>>
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-bootstrapwith]]
|
|
|
|
|
=== `@BootstrapWith`
|
|
|
|
|
|
|
|
|
|
`@BootstrapWith` is a class-level annotation that you can use to configure how the Spring
|
|
|
|
|
TestContext Framework is bootstrapped. Specifically, you can use `@BootstrapWith` to
|
|
|
|
|
specify a custom `TestContextBootstrapper`. See the section on
|
|
|
|
|
<<testcontext-bootstrapping, bootstrapping the TestContext framework>> for further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-contextconfiguration]]
|
|
|
|
|
=== `@ContextConfiguration`
|
|
|
|
|
|
|
|
|
|
`@ContextConfiguration` defines class-level metadata that is used to determine how to
|
|
|
|
|
load and configure an `ApplicationContext` for integration tests. Specifically,
|
|
|
|
|
`@ContextConfiguration` declares the application context resource `locations` or the
|
|
|
|
|
component `classes` used to load the context.
|
|
|
|
|
|
|
|
|
|
Resource locations are typically XML configuration files or Groovy scripts located in the
|
|
|
|
|
classpath, while component classes are typically `@Configuration` classes. However,
|
|
|
|
|
resource locations can also refer to files and scripts in the file system, and component
|
|
|
|
|
classes can be `@Component` classes, `@Service` classes, and so on. See
|
|
|
|
|
<<testcontext-ctx-management-javaconfig-component-classes>> for further details.
|
|
|
|
|
|
|
|
|
|
The following example shows a `@ContextConfiguration` annotation that refers to an XML
|
|
|
|
|
file:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration("/test-config.xml") // <1>
|
|
|
|
|
class XmlApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Referring to an XML file.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration("/test-config.xml") // <1>
|
|
|
|
|
class XmlApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Referring to an XML file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following example shows a `@ContextConfiguration` annotation that refers to a class:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration(classes = TestConfig.class) // <1>
|
|
|
|
|
class ConfigClassApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Referring to a class.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration(classes = [TestConfig::class]) // <1>
|
|
|
|
|
class ConfigClassApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Referring to a class.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
As an alternative or in addition to declaring resource locations or component classes,
|
|
|
|
|
you can use `@ContextConfiguration` to declare `ApplicationContextInitializer` classes.
|
|
|
|
|
The following example shows such a case:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration(initializers = CustomContextInitializer.class) // <1>
|
|
|
|
|
class ContextInitializerTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Declaring an initializer class.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration(initializers = [CustomContextInitializer::class]) // <1>
|
|
|
|
|
class ContextInitializerTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Declaring an initializer class.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
You can optionally use `@ContextConfiguration` to declare the `ContextLoader` strategy as
|
|
|
|
|
well. Note, however, that you typically do not need to explicitly configure the loader,
|
|
|
|
|
since the default loader supports `initializers` and either resource `locations` or
|
|
|
|
|
component `classes`.
|
|
|
|
|
|
|
|
|
|
The following example uses both a location and a loader:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class) // <1>
|
|
|
|
|
class CustomLoaderXmlApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Configuring both a location and a custom loader.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration("/test-context.xml", loader = CustomContextLoader::class) // <1>
|
|
|
|
|
class CustomLoaderXmlApplicationContextTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Configuring both a location and a custom loader.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NOTE: `@ContextConfiguration` provides support for inheriting resource locations or
|
|
|
|
|
configuration classes as well as context initializers that are declared by superclasses
|
|
|
|
|
or enclosing classes.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>>,
|
|
|
|
|
<<testcontext-junit-jupiter-nested-test-configuration>>, and the `@ContextConfiguration`
|
|
|
|
|
javadocs for further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-webappconfiguration]]
|
|
|
|
|
=== `@WebAppConfiguration`
|
|
|
|
|
|
|
|
|
|
`@WebAppConfiguration` is a class-level annotation that you can use to declare that the
|
|
|
|
|
`ApplicationContext` loaded for an integration test should be a `WebApplicationContext`.
|
|
|
|
|
The mere presence of `@WebAppConfiguration` on a test class ensures that a
|
|
|
|
|
`WebApplicationContext` is loaded for the test, using the default value of
|
|
|
|
|
`"file:src/main/webapp"` for the path to the root of the web application (that is, the
|
|
|
|
|
resource base path). The resource base path is used behind the scenes to create a
|
|
|
|
|
`MockServletContext`, which serves as the `ServletContext` for the test's
|
|
|
|
|
`WebApplicationContext`.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@WebAppConfiguration` annotation:
|
|
|
|
|
|
2022-11-28 21:47:56 +08:00
|
|
|
|
--
|
2022-11-19 00:33:40 +08:00
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@WebAppConfiguration // <1>
|
|
|
|
|
class WebAppTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
2022-11-28 21:47:56 +08:00
|
|
|
|
<1> The `@WebAppConfiguration` annotation.
|
2022-11-19 00:33:40 +08:00
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@WebAppConfiguration // <1>
|
|
|
|
|
class WebAppTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> The `@WebAppConfiguration` annotation.
|
2022-11-28 21:47:56 +08:00
|
|
|
|
--
|
2022-11-19 00:33:40 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
To override the default, you can specify a different base resource path by using the
|
|
|
|
|
implicit `value` attribute. Both `classpath:` and `file:` resource prefixes are
|
|
|
|
|
supported. If no resource prefix is supplied, the path is assumed to be a file system
|
|
|
|
|
resource. The following example shows how to specify a classpath resource:
|
|
|
|
|
|
2022-11-28 21:47:56 +08:00
|
|
|
|
--
|
2022-11-19 00:33:40 +08:00
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@WebAppConfiguration("classpath:test-web-resources") // <1>
|
|
|
|
|
class WebAppTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specifying a classpath resource.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@WebAppConfiguration("classpath:test-web-resources") // <1>
|
|
|
|
|
class WebAppTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specifying a classpath resource.
|
2022-11-28 21:47:56 +08:00
|
|
|
|
--
|
2022-11-19 00:33:40 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note that `@WebAppConfiguration` must be used in conjunction with
|
|
|
|
|
`@ContextConfiguration`, either within a single test class or within a test class
|
|
|
|
|
hierarchy. See the
|
|
|
|
|
{api-spring-framework}/test/context/web/WebAppConfiguration.html[`@WebAppConfiguration`]
|
|
|
|
|
javadoc for further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-contexthierarchy]]
|
|
|
|
|
=== `@ContextHierarchy`
|
|
|
|
|
|
|
|
|
|
`@ContextHierarchy` is a class-level annotation that is used to define a hierarchy of
|
|
|
|
|
`ApplicationContext` instances for integration tests. `@ContextHierarchy` should be
|
|
|
|
|
declared with a list of one or more `@ContextConfiguration` instances, each of which
|
|
|
|
|
defines a level in the context hierarchy. The following examples demonstrate the use of
|
|
|
|
|
`@ContextHierarchy` within a single test class (`@ContextHierarchy` can also be used
|
|
|
|
|
within a test class hierarchy):
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration("/parent-config.xml"),
|
|
|
|
|
@ContextConfiguration("/child-config.xml")
|
|
|
|
|
})
|
|
|
|
|
class ContextHierarchyTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextHierarchy(
|
|
|
|
|
ContextConfiguration("/parent-config.xml"),
|
|
|
|
|
ContextConfiguration("/child-config.xml"))
|
|
|
|
|
class ContextHierarchyTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@WebAppConfiguration
|
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration(classes = AppConfig.class),
|
|
|
|
|
@ContextConfiguration(classes = WebConfig.class)
|
|
|
|
|
})
|
|
|
|
|
class WebIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@WebAppConfiguration
|
|
|
|
|
@ContextHierarchy(
|
|
|
|
|
ContextConfiguration(classes = [AppConfig::class]),
|
|
|
|
|
ContextConfiguration(classes = [WebConfig::class]))
|
|
|
|
|
class WebIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
If you need to merge or override the configuration for a given level of the context
|
|
|
|
|
hierarchy within a test class hierarchy, you must explicitly name that level by supplying
|
|
|
|
|
the same value to the `name` attribute in `@ContextConfiguration` at each corresponding
|
|
|
|
|
level in the class hierarchy. See <<testcontext-ctx-management-ctx-hierarchies>> and the
|
|
|
|
|
{api-spring-framework}/test/context/ContextHierarchy.html[`@ContextHierarchy`] javadoc
|
|
|
|
|
for further examples.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-activeprofiles]]
|
|
|
|
|
=== `@ActiveProfiles`
|
|
|
|
|
|
|
|
|
|
`@ActiveProfiles` is a class-level annotation that is used to declare which bean
|
|
|
|
|
definition profiles should be active when loading an `ApplicationContext` for an
|
|
|
|
|
integration test.
|
|
|
|
|
|
|
|
|
|
The following example indicates that the `dev` profile should be active:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@ActiveProfiles("dev") // <1>
|
|
|
|
|
class DeveloperTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Indicate that the `dev` profile should be active.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@ActiveProfiles("dev") // <1>
|
|
|
|
|
class DeveloperTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Indicate that the `dev` profile should be active.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following example indicates that both the `dev` and the `integration` profiles should
|
|
|
|
|
be active:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@ActiveProfiles({"dev", "integration"}) // <1>
|
|
|
|
|
class DeveloperIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Indicate that the `dev` and `integration` profiles should be active.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@ActiveProfiles(["dev", "integration"]) // <1>
|
|
|
|
|
class DeveloperIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Indicate that the `dev` and `integration` profiles should be active.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NOTE: `@ActiveProfiles` provides support for inheriting active bean definition profiles
|
|
|
|
|
declared by superclasses and enclosing classes by default. You can also resolve active
|
|
|
|
|
bean definition profiles programmatically by implementing a custom
|
|
|
|
|
<<testcontext-ctx-management-env-profiles-ActiveProfilesResolver, `ActiveProfilesResolver`>>
|
|
|
|
|
and registering it by using the `resolver` attribute of `@ActiveProfiles`.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management-env-profiles>>,
|
|
|
|
|
<<testcontext-junit-jupiter-nested-test-configuration>>, and the
|
|
|
|
|
{api-spring-framework}/test/context/ActiveProfiles.html[`@ActiveProfiles`] javadoc for
|
|
|
|
|
examples and further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-testpropertysource]]
|
|
|
|
|
=== `@TestPropertySource`
|
|
|
|
|
|
|
|
|
|
`@TestPropertySource` is a class-level annotation that you can use to configure the
|
|
|
|
|
locations of properties files and inlined properties to be added to the set of
|
|
|
|
|
`PropertySources` in the `Environment` for an `ApplicationContext` loaded for an
|
|
|
|
|
integration test.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to declare a properties file from the classpath:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestPropertySource("/test.properties") // <1>
|
|
|
|
|
class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Get properties from `test.properties` in the root of the classpath.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestPropertySource("/test.properties") // <1>
|
|
|
|
|
class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Get properties from `test.properties` in the root of the classpath.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to declare inlined properties:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" }) // <1>
|
|
|
|
|
class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Declare `timezone` and `port` properties.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) // <1>
|
|
|
|
|
class MyIntegrationTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Declare `timezone` and `port` properties.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management-property-sources>> for examples and further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-dynamicpropertysource]]
|
|
|
|
|
=== `@DynamicPropertySource`
|
|
|
|
|
|
|
|
|
|
`@DynamicPropertySource` is a method-level annotation that you can use to register
|
|
|
|
|
_dynamic_ properties to be added to the set of `PropertySources` in the `Environment` for
|
|
|
|
|
an `ApplicationContext` loaded for an integration test. Dynamic properties are useful
|
|
|
|
|
when you do not know the value of the properties upfront – for example, if the properties
|
|
|
|
|
are managed by an external resource such as for a container managed by the
|
|
|
|
|
https://www.testcontainers.org/[Testcontainers] project.
|
|
|
|
|
|
|
|
|
|
The following example demonstrates how to register a dynamic property:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
class MyIntegrationTests {
|
|
|
|
|
|
|
|
|
|
static MyExternalServer server = // ...
|
|
|
|
|
|
|
|
|
|
@DynamicPropertySource // <1>
|
|
|
|
|
static void dynamicProperties(DynamicPropertyRegistry registry) { // <2>
|
|
|
|
|
registry.add("server.port", server::getPort); // <3>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Annotate a `static` method with `@DynamicPropertySource`.
|
|
|
|
|
<2> Accept a `DynamicPropertyRegistry` as an argument.
|
|
|
|
|
<3> Register a dynamic `server.port` property to be retrieved lazily from the server.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
class MyIntegrationTests {
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
val server: MyExternalServer = // ...
|
|
|
|
|
|
|
|
|
|
@DynamicPropertySource // <1>
|
|
|
|
|
@JvmStatic
|
|
|
|
|
fun dynamicProperties(registry: DynamicPropertyRegistry) { // <2>
|
|
|
|
|
registry.add("server.port", server::getPort) // <3>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Annotate a `static` method with `@DynamicPropertySource`.
|
|
|
|
|
<2> Accept a `DynamicPropertyRegistry` as an argument.
|
|
|
|
|
<3> Register a dynamic `server.port` property to be retrieved lazily from the server.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management-dynamic-property-sources>> for further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-dirtiescontext]]
|
|
|
|
|
=== `@DirtiesContext`
|
|
|
|
|
|
|
|
|
|
`@DirtiesContext` indicates that the underlying Spring `ApplicationContext` has been
|
|
|
|
|
dirtied during the execution of a test (that is, the test modified or corrupted it in
|
|
|
|
|
some manner -- for example, by changing the state of a singleton bean) and should be
|
|
|
|
|
closed. When an application context is marked as dirty, it is removed from the testing
|
|
|
|
|
framework's cache and closed. As a consequence, the underlying Spring container is
|
|
|
|
|
rebuilt for any subsequent test that requires a context with the same configuration
|
|
|
|
|
metadata.
|
|
|
|
|
|
|
|
|
|
You can use `@DirtiesContext` as both a class-level and a method-level annotation within
|
|
|
|
|
the same class or class hierarchy. In such scenarios, the `ApplicationContext` is marked
|
|
|
|
|
as dirty before or after any such annotated method as well as before or after the current
|
|
|
|
|
test class, depending on the configured `methodMode` and `classMode`.
|
|
|
|
|
|
|
|
|
|
The following examples explain when the context would be dirtied for various
|
|
|
|
|
configuration scenarios:
|
|
|
|
|
|
|
|
|
|
* Before the current test class, when declared on a class with class mode set to
|
|
|
|
|
`BEFORE_CLASS`.
|
|
|
|
|
+
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(classMode = BEFORE_CLASS) // <1>
|
|
|
|
|
class FreshContextTests {
|
|
|
|
|
// some tests that require a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context before the current test class.
|
|
|
|
|
+
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(classMode = BEFORE_CLASS) // <1>
|
|
|
|
|
class FreshContextTests {
|
|
|
|
|
// some tests that require a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context before the current test class.
|
|
|
|
|
|
|
|
|
|
* After the current test class, when declared on a class with class mode set to
|
|
|
|
|
`AFTER_CLASS` (i.e., the default class mode).
|
|
|
|
|
+
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext // <1>
|
|
|
|
|
class ContextDirtyingTests {
|
|
|
|
|
// some tests that result in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context after the current test class.
|
|
|
|
|
+
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext // <1>
|
|
|
|
|
class ContextDirtyingTests {
|
|
|
|
|
// some tests that result in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context after the current test class.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* Before each test method in the current test class, when declared on a class with class
|
|
|
|
|
mode set to `BEFORE_EACH_TEST_METHOD.`
|
|
|
|
|
+
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) // <1>
|
|
|
|
|
class FreshContextTests {
|
|
|
|
|
// some tests that require a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context before each test method.
|
|
|
|
|
+
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) // <1>
|
|
|
|
|
class FreshContextTests {
|
|
|
|
|
// some tests that require a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context before each test method.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* After each test method in the current test class, when declared on a class with class
|
|
|
|
|
mode set to `AFTER_EACH_TEST_METHOD.`
|
|
|
|
|
+
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) // <1>
|
|
|
|
|
class ContextDirtyingTests {
|
|
|
|
|
// some tests that result in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context after each test method.
|
|
|
|
|
+
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) // <1>
|
|
|
|
|
class ContextDirtyingTests {
|
|
|
|
|
// some tests that result in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context after each test method.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* Before the current test, when declared on a method with the method mode set to
|
|
|
|
|
`BEFORE_METHOD`.
|
|
|
|
|
+
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(methodMode = BEFORE_METHOD) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
void testProcessWhichRequiresFreshAppCtx() {
|
|
|
|
|
// some logic that requires a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context before the current test method.
|
|
|
|
|
+
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext(methodMode = BEFORE_METHOD) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessWhichRequiresFreshAppCtx() {
|
|
|
|
|
// some logic that requires a new Spring container
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context before the current test method.
|
|
|
|
|
|
|
|
|
|
* After the current test, when declared on a method with the method mode set to
|
|
|
|
|
`AFTER_METHOD` (i.e., the default method mode).
|
|
|
|
|
+
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext // <1>
|
|
|
|
|
@Test
|
|
|
|
|
void testProcessWhichDirtiesAppCtx() {
|
|
|
|
|
// some logic that results in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context after the current test method.
|
|
|
|
|
+
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@DirtiesContext // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessWhichDirtiesAppCtx() {
|
|
|
|
|
// some logic that results in the Spring container being dirtied
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Dirty the context after the current test method.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If you use `@DirtiesContext` in a test whose context is configured as part of a context
|
|
|
|
|
hierarchy with `@ContextHierarchy`, you can use the `hierarchyMode` flag to control how
|
|
|
|
|
the context cache is cleared. By default, an exhaustive algorithm is used to clear the
|
|
|
|
|
context cache, including not only the current level but also all other context
|
|
|
|
|
hierarchies that share an ancestor context common to the current test. All
|
|
|
|
|
`ApplicationContext` instances that reside in a sub-hierarchy of the common ancestor
|
|
|
|
|
context are removed from the context cache and closed. If the exhaustive algorithm is
|
|
|
|
|
overkill for a particular use case, you can specify the simpler current level algorithm,
|
|
|
|
|
as the following example shows.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextHierarchy({
|
|
|
|
|
@ContextConfiguration("/parent-config.xml"),
|
|
|
|
|
@ContextConfiguration("/child-config.xml")
|
|
|
|
|
})
|
|
|
|
|
class BaseTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ExtendedTests extends BaseTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DirtiesContext(hierarchyMode = CURRENT_LEVEL) // <1>
|
|
|
|
|
void test() {
|
|
|
|
|
// some logic that results in the child context being dirtied
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Use the current-level algorithm.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextHierarchy(
|
|
|
|
|
ContextConfiguration("/parent-config.xml"),
|
|
|
|
|
ContextConfiguration("/child-config.xml"))
|
|
|
|
|
open class BaseTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ExtendedTests : BaseTests() {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DirtiesContext(hierarchyMode = CURRENT_LEVEL) // <1>
|
|
|
|
|
fun test() {
|
|
|
|
|
// some logic that results in the child context being dirtied
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Use the current-level algorithm.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For further details regarding the `EXHAUSTIVE` and `CURRENT_LEVEL` algorithms, see the
|
|
|
|
|
{api-spring-framework}/test/annotation/DirtiesContext.HierarchyMode.html[`DirtiesContext.HierarchyMode`]
|
|
|
|
|
javadoc.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-testexecutionlisteners]]
|
|
|
|
|
=== `@TestExecutionListeners`
|
|
|
|
|
|
|
|
|
|
`@TestExecutionListeners` is used to register listeners for a particular test class, its
|
|
|
|
|
subclasses, and its nested classes. If you wish to register a listener globally, you
|
|
|
|
|
should register it via the automatic discovery mechanism described in
|
|
|
|
|
<<testcontext-tel-config>>.
|
|
|
|
|
|
|
|
|
|
The following example shows how to register two `TestExecutionListener` implementations:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) // <1>
|
|
|
|
|
class CustomTestExecutionListenerTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Register two `TestExecutionListener` implementations.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ContextConfiguration
|
|
|
|
|
@TestExecutionListeners(CustomTestExecutionListener::class, AnotherTestExecutionListener::class) // <1>
|
|
|
|
|
class CustomTestExecutionListenerTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Register two `TestExecutionListener` implementations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
By default, `@TestExecutionListeners` provides support for inheriting listeners from
|
|
|
|
|
superclasses or enclosing classes. See
|
|
|
|
|
<<testcontext-junit-jupiter-nested-test-configuration>> and the
|
|
|
|
|
{api-spring-framework}/test/context/TestExecutionListeners.html[`@TestExecutionListeners`
|
|
|
|
|
javadoc] for an example and further details. If you discover that you need to switch
|
|
|
|
|
back to using the default `TestExecutionListener` implementations, see the note
|
|
|
|
|
in <<testcontext-tel-config-registering-tels>>.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-recordapplicationevents]]
|
|
|
|
|
=== `@RecordApplicationEvents`
|
|
|
|
|
|
|
|
|
|
`@RecordApplicationEvents` is a class-level annotation that is used to instruct the
|
|
|
|
|
_Spring TestContext Framework_ to record all application events that are published in the
|
|
|
|
|
`ApplicationContext` during the execution of a single test.
|
|
|
|
|
|
|
|
|
|
The recorded events can be accessed via the `ApplicationEvents` API within tests.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-application-events>> and the
|
|
|
|
|
{api-spring-framework}/test/context/event/RecordApplicationEvents.html[`@RecordApplicationEvents`
|
|
|
|
|
javadoc] for an example and further details.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-commit]]
|
|
|
|
|
=== `@Commit`
|
|
|
|
|
|
|
|
|
|
`@Commit` indicates that the transaction for a transactional test method should be
|
|
|
|
|
committed after the test method has completed. You can use `@Commit` as a direct
|
|
|
|
|
replacement for `@Rollback(false)` to more explicitly convey the intent of the code.
|
|
|
|
|
Analogous to `@Rollback`, `@Commit` can also be declared as a class-level or method-level
|
|
|
|
|
annotation.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@Commit` annotation:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Commit // <1>
|
|
|
|
|
@Test
|
|
|
|
|
void testProcessWithoutRollback() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Commit the result of the test to the database.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Commit // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessWithoutRollback() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Commit the result of the test to the database.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-rollback]]
|
|
|
|
|
=== `@Rollback`
|
|
|
|
|
|
|
|
|
|
`@Rollback` indicates whether the transaction for a transactional test method should be
|
|
|
|
|
rolled back after the test method has completed. If `true`, the transaction is rolled
|
|
|
|
|
back. Otherwise, the transaction is committed (see also
|
|
|
|
|
<<spring-testing-annotation-commit>>). Rollback for integration tests in the Spring
|
|
|
|
|
TestContext Framework defaults to `true` even if `@Rollback` is not explicitly declared.
|
|
|
|
|
|
|
|
|
|
When declared as a class-level annotation, `@Rollback` defines the default rollback
|
|
|
|
|
semantics for all test methods within the test class hierarchy. When declared as a
|
|
|
|
|
method-level annotation, `@Rollback` defines rollback semantics for the specific test
|
|
|
|
|
method, potentially overriding class-level `@Rollback` or `@Commit` semantics.
|
|
|
|
|
|
|
|
|
|
The following example causes a test method's result to not be rolled back (that is, the
|
|
|
|
|
result is committed to the database):
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Rollback(false) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
void testProcessWithoutRollback() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Do not roll back the result.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Rollback(false) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessWithoutRollback() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Do not roll back the result.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-beforetransaction]]
|
|
|
|
|
=== `@BeforeTransaction`
|
|
|
|
|
|
|
|
|
|
`@BeforeTransaction` indicates that the annotated `void` method should be run before a
|
|
|
|
|
transaction is started, for test methods that have been configured to run within a
|
|
|
|
|
transaction by using Spring's `@Transactional` annotation. `@BeforeTransaction` methods
|
|
|
|
|
are not required to be `public` and may be declared on Java 8-based interface default
|
|
|
|
|
methods.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@BeforeTransaction` annotation:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@BeforeTransaction // <1>
|
|
|
|
|
void beforeTransaction() {
|
|
|
|
|
// logic to be run before a transaction is started
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this method before a transaction.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@BeforeTransaction // <1>
|
|
|
|
|
fun beforeTransaction() {
|
|
|
|
|
// logic to be run before a transaction is started
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this method before a transaction.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-aftertransaction]]
|
|
|
|
|
=== `@AfterTransaction`
|
|
|
|
|
|
|
|
|
|
`@AfterTransaction` indicates that the annotated `void` method should be run after a
|
|
|
|
|
transaction is ended, for test methods that have been configured to run within a
|
|
|
|
|
transaction by using Spring's `@Transactional` annotation. `@AfterTransaction` methods
|
|
|
|
|
are not required to be `public` and may be declared on Java 8-based interface default
|
|
|
|
|
methods.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@AfterTransaction // <1>
|
|
|
|
|
void afterTransaction() {
|
|
|
|
|
// logic to be run after a transaction has ended
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this method after a transaction.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@AfterTransaction // <1>
|
|
|
|
|
fun afterTransaction() {
|
|
|
|
|
// logic to be run after a transaction has ended
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this method after a transaction.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-sql]]
|
|
|
|
|
=== `@Sql`
|
|
|
|
|
|
|
|
|
|
`@Sql` is used to annotate a test class or test method to configure SQL scripts to be run
|
|
|
|
|
against a given database during integration tests. The following example shows how to use
|
|
|
|
|
it:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql({"/test-schema.sql", "/test-user-data.sql"}) // <1>
|
|
|
|
|
void userTest() {
|
|
|
|
|
// run code that relies on the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run two scripts for this test.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql("/test-schema.sql", "/test-user-data.sql") // <1>
|
|
|
|
|
fun userTest() {
|
|
|
|
|
// run code that relies on the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run two scripts for this test.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-executing-sql-declaratively>> for further details.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-sqlconfig]]
|
|
|
|
|
=== `@SqlConfig`
|
|
|
|
|
|
|
|
|
|
`@SqlConfig` defines metadata that is used to determine how to parse and run SQL scripts
|
|
|
|
|
configured with the `@Sql` annotation. The following example shows how to use it:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql(
|
|
|
|
|
scripts = "/test-user-data.sql",
|
|
|
|
|
config = @SqlConfig(commentPrefix = "`", separator = "@@") // <1>
|
|
|
|
|
)
|
|
|
|
|
void userTest() {
|
|
|
|
|
// run code that relies on the test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the comment prefix and the separator in SQL scripts.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@Sql("/test-user-data.sql", config = SqlConfig(commentPrefix = "`", separator = "@@")) // <1>
|
|
|
|
|
fun userTest() {
|
|
|
|
|
// run code that relies on the test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the comment prefix and the separator in SQL scripts.
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-sqlmergemode]]
|
|
|
|
|
=== `@SqlMergeMode`
|
|
|
|
|
|
|
|
|
|
`@SqlMergeMode` is used to annotate a test class or test method to configure whether
|
|
|
|
|
method-level `@Sql` declarations are merged with class-level `@Sql` declarations. If
|
|
|
|
|
`@SqlMergeMode` is not declared on a test class or test method, the `OVERRIDE` merge mode
|
|
|
|
|
will be used by default. With the `OVERRIDE` mode, method-level `@Sql` declarations will
|
|
|
|
|
effectively override class-level `@Sql` declarations.
|
|
|
|
|
|
|
|
|
|
Note that a method-level `@SqlMergeMode` declaration overrides a class-level declaration.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use `@SqlMergeMode` at the class level.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class)
|
|
|
|
|
@Sql("/test-schema.sql")
|
|
|
|
|
@SqlMergeMode(MERGE) // <1>
|
|
|
|
|
class UserTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@Sql("/user-test-data-001.sql")
|
|
|
|
|
void standardUserProfile() {
|
|
|
|
|
// run code that relies on test data set 001
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the `@Sql` merge mode to `MERGE` for all test methods in the class.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig::class)
|
|
|
|
|
@Sql("/test-schema.sql")
|
|
|
|
|
@SqlMergeMode(MERGE) // <1>
|
|
|
|
|
class UserTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@Sql("/user-test-data-001.sql")
|
|
|
|
|
fun standardUserProfile() {
|
|
|
|
|
// run code that relies on test data set 001
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the `@Sql` merge mode to `MERGE` for all test methods in the class.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use `@SqlMergeMode` at the method level.
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class)
|
|
|
|
|
@Sql("/test-schema.sql")
|
|
|
|
|
class UserTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@Sql("/user-test-data-001.sql")
|
|
|
|
|
@SqlMergeMode(MERGE) // <1>
|
|
|
|
|
void standardUserProfile() {
|
|
|
|
|
// run code that relies on test data set 001
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the `@Sql` merge mode to `MERGE` for a specific test method.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig::class)
|
|
|
|
|
@Sql("/test-schema.sql")
|
|
|
|
|
class UserTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@Sql("/user-test-data-001.sql")
|
|
|
|
|
@SqlMergeMode(MERGE) // <1>
|
|
|
|
|
fun standardUserProfile() {
|
|
|
|
|
// run code that relies on test data set 001
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the `@Sql` merge mode to `MERGE` for a specific test method.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[spring-testing-annotation-sqlgroup]]
|
|
|
|
|
=== `@SqlGroup`
|
|
|
|
|
|
|
|
|
|
`@SqlGroup` is a container annotation that aggregates several `@Sql` annotations. You can
|
|
|
|
|
use `@SqlGroup` natively to declare several nested `@Sql` annotations, or you can use it
|
|
|
|
|
in conjunction with Java 8's support for repeatable annotations, where `@Sql` can be
|
|
|
|
|
declared several times on the same class or method, implicitly generating this container
|
|
|
|
|
annotation. The following example shows how to declare an SQL group:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@SqlGroup({ // <1>
|
|
|
|
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
|
|
|
|
|
@Sql("/test-user-data.sql")
|
2022-11-28 21:47:56 +08:00
|
|
|
|
})
|
2022-11-19 00:33:40 +08:00
|
|
|
|
void userTest() {
|
|
|
|
|
// run code that uses the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Declare a group of SQL scripts.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Test
|
|
|
|
|
@SqlGroup( // <1>
|
|
|
|
|
Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`")),
|
|
|
|
|
Sql("/test-user-data.sql"))
|
|
|
|
|
fun userTest() {
|
|
|
|
|
// run code that uses the test schema and test data
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Declare a group of SQL scripts.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit4]]
|
|
|
|
|
== Spring JUnit 4 Testing Annotations
|
|
|
|
|
|
|
|
|
|
The following annotations are supported only when used in conjunction with the
|
|
|
|
|
<<testcontext-junit4-runner, SpringRunner>>, <<testcontext-junit4-rules, Spring's JUnit 4
|
|
|
|
|
rules>>, or <<testcontext-support-classes-junit4, Spring's JUnit 4 support classes>>:
|
|
|
|
|
|
|
|
|
|
* <<integration-testing-annotations-junit4-ifprofilevalue>>
|
|
|
|
|
* <<integration-testing-annotations-junit4-profilevaluesourceconfiguration>>
|
|
|
|
|
* <<integration-testing-annotations-junit4-timed>>
|
|
|
|
|
* <<integration-testing-annotations-junit4-repeat>>
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit4-ifprofilevalue]]
|
|
|
|
|
=== `@IfProfileValue`
|
|
|
|
|
|
|
|
|
|
`@IfProfileValue` indicates that the annotated test is enabled for a specific testing
|
|
|
|
|
environment. If the configured `ProfileValueSource` returns a matching `value` for the
|
|
|
|
|
provided `name`, the test is enabled. Otherwise, the test is disabled and, effectively,
|
|
|
|
|
ignored.
|
|
|
|
|
|
|
|
|
|
You can apply `@IfProfileValue` at the class level, the method level, or both.
|
|
|
|
|
Class-level usage of `@IfProfileValue` takes precedence over method-level usage for any
|
|
|
|
|
methods within that class or its subclasses. Specifically, a test is enabled if it is
|
|
|
|
|
enabled both at the class level and at the method level. The absence of `@IfProfileValue`
|
|
|
|
|
means the test is implicitly enabled. This is analogous to the semantics of JUnit 4's
|
|
|
|
|
`@Ignore` annotation, except that the presence of `@Ignore` always disables a test.
|
|
|
|
|
|
|
|
|
|
The following example shows a test that has an `@IfProfileValue` annotation:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@IfProfileValue(name="java.vendor", value="Oracle Corporation") // <1>
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWhichRunsOnlyOnOracleJvm() {
|
|
|
|
|
// some logic that should run only on Java VMs from Oracle Corporation
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this test only when the Java vendor is "Oracle Corporation".
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@IfProfileValue(name="java.vendor", value="Oracle Corporation") // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessWhichRunsOnlyOnOracleJvm() {
|
|
|
|
|
// some logic that should run only on Java VMs from Oracle Corporation
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this test only when the Java vendor is "Oracle Corporation".
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alternatively, you can configure `@IfProfileValue` with a list of `values` (with `OR`
|
|
|
|
|
semantics) to achieve TestNG-like support for test groups in a JUnit 4 environment.
|
|
|
|
|
Consider the following example:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"}) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
|
|
|
|
|
// some logic that should run only for unit and integration test groups
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this test for unit tests and integration tests.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@IfProfileValue(name="test-groups", values=["unit-tests", "integration-tests"]) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessWhichRunsForUnitOrIntegrationTestGroups() {
|
|
|
|
|
// some logic that should run only for unit and integration test groups
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Run this test for unit tests and integration tests.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit4-profilevaluesourceconfiguration]]
|
|
|
|
|
=== `@ProfileValueSourceConfiguration`
|
|
|
|
|
|
|
|
|
|
`@ProfileValueSourceConfiguration` is a class-level annotation that specifies what type
|
|
|
|
|
of `ProfileValueSource` to use when retrieving profile values configured through the
|
|
|
|
|
`@IfProfileValue` annotation. If `@ProfileValueSourceConfiguration` is not declared for a
|
|
|
|
|
test, `SystemProfileValueSource` is used by default. The following example shows how to
|
|
|
|
|
use `@ProfileValueSourceConfiguration`:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@ProfileValueSourceConfiguration(CustomProfileValueSource.class) // <1>
|
|
|
|
|
public class CustomProfileValueSourceTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Use a custom profile value source.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@ProfileValueSourceConfiguration(CustomProfileValueSource::class) // <1>
|
|
|
|
|
class CustomProfileValueSourceTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Use a custom profile value source.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit4-timed]]
|
|
|
|
|
=== `@Timed`
|
|
|
|
|
|
|
|
|
|
`@Timed` indicates that the annotated test method must finish execution in a specified
|
|
|
|
|
time period (in milliseconds). If the text execution time exceeds the specified time
|
|
|
|
|
period, the test fails.
|
|
|
|
|
|
|
|
|
|
The time period includes running the test method itself, any repetitions of the test (see
|
|
|
|
|
`@Repeat`), as well as any setting up or tearing down of the test fixture. The following
|
|
|
|
|
example shows how to use it:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Timed(millis = 1000) // <1>
|
|
|
|
|
public void testProcessWithOneSecondTimeout() {
|
|
|
|
|
// some logic that should not take longer than 1 second to run
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the time period for the test to one second.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Timed(millis = 1000) // <1>
|
|
|
|
|
fun testProcessWithOneSecondTimeout() {
|
|
|
|
|
// some logic that should not take longer than 1 second to run
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Set the time period for the test to one second.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Spring's `@Timed` annotation has different semantics than JUnit 4's `@Test(timeout=...)`
|
|
|
|
|
support. Specifically, due to the manner in which JUnit 4 handles test execution timeouts
|
|
|
|
|
(that is, by executing the test method in a separate `Thread`), `@Test(timeout=...)`
|
|
|
|
|
preemptively fails the test if the test takes too long. Spring's `@Timed`, on the other
|
|
|
|
|
hand, does not preemptively fail the test but rather waits for the test to complete
|
|
|
|
|
before failing.
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit4-repeat]]
|
|
|
|
|
=== `@Repeat`
|
|
|
|
|
|
|
|
|
|
`@Repeat` indicates that the annotated test method must be run repeatedly. The number of
|
|
|
|
|
times that the test method is to be run is specified in the annotation.
|
|
|
|
|
|
|
|
|
|
The scope of execution to be repeated includes execution of the test method itself as
|
|
|
|
|
well as any setting up or tearing down of the test fixture. When used with the
|
|
|
|
|
<<testcontext-junit4-rules, `SpringMethodRule`>>, the scope additionally includes
|
|
|
|
|
preparation of the test instance by `TestExecutionListener` implementations. The
|
|
|
|
|
following example shows how to use the `@Repeat` annotation:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Repeat(10) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
public void testProcessRepeatedly() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Repeat this test ten times.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Repeat(10) // <1>
|
|
|
|
|
@Test
|
|
|
|
|
fun testProcessRepeatedly() {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Repeat this test ten times.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit-jupiter]]
|
|
|
|
|
== Spring JUnit Jupiter Testing Annotations
|
|
|
|
|
|
|
|
|
|
The following annotations are supported when used in conjunction with the
|
|
|
|
|
<<testcontext-junit-jupiter-extension, `SpringExtension`>> and JUnit Jupiter
|
|
|
|
|
(that is, the programming model in JUnit 5):
|
|
|
|
|
|
|
|
|
|
* <<integration-testing-annotations-junit-jupiter-springjunitconfig>>
|
|
|
|
|
* <<integration-testing-annotations-junit-jupiter-springjunitwebconfig>>
|
|
|
|
|
* <<integration-testing-annotations-testconstructor>>
|
|
|
|
|
* <<integration-testing-annotations-nestedtestconfiguration>>
|
|
|
|
|
* <<integration-testing-annotations-junit-jupiter-enabledif>>
|
|
|
|
|
* <<integration-testing-annotations-junit-jupiter-disabledif>>
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit-jupiter-springjunitconfig]]
|
|
|
|
|
=== `@SpringJUnitConfig`
|
|
|
|
|
|
|
|
|
|
`@SpringJUnitConfig` is a composed annotation that combines
|
|
|
|
|
`@ExtendWith(SpringExtension.class)` from JUnit Jupiter with `@ContextConfiguration` from
|
|
|
|
|
the Spring TestContext Framework. It can be used at the class level as a drop-in
|
|
|
|
|
replacement for `@ContextConfiguration`. With regard to configuration options, the only
|
|
|
|
|
difference between `@ContextConfiguration` and `@SpringJUnitConfig` is that component
|
|
|
|
|
classes may be declared with the `value` attribute in `@SpringJUnitConfig`.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@SpringJUnitConfig` annotation to specify a
|
|
|
|
|
configuration class:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig.class) // <1>
|
|
|
|
|
class ConfigurationClassJUnitJupiterSpringTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the configuration class.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(TestConfig::class) // <1>
|
|
|
|
|
class ConfigurationClassJUnitJupiterSpringTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the configuration class.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@SpringJUnitConfig` annotation to specify the
|
|
|
|
|
location of a configuration file:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(locations = "/test-config.xml") // <1>
|
|
|
|
|
class XmlJUnitJupiterSpringTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the location of a configuration file.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitConfig(locations = ["/test-config.xml"]) // <1>
|
|
|
|
|
class XmlJUnitJupiterSpringTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the location of a configuration file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>> as well as the javadoc for
|
|
|
|
|
{api-spring-framework}/test/context/junit/jupiter/SpringJUnitConfig.html[`@SpringJUnitConfig`]
|
|
|
|
|
and `@ContextConfiguration` for further details.
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit-jupiter-springjunitwebconfig]]
|
|
|
|
|
=== `@SpringJUnitWebConfig`
|
|
|
|
|
|
|
|
|
|
`@SpringJUnitWebConfig` is a composed annotation that combines
|
|
|
|
|
`@ExtendWith(SpringExtension.class)` from JUnit Jupiter with `@ContextConfiguration` and
|
|
|
|
|
`@WebAppConfiguration` from the Spring TestContext Framework. You can use it at the class
|
|
|
|
|
level as a drop-in replacement for `@ContextConfiguration` and `@WebAppConfiguration`.
|
|
|
|
|
With regard to configuration options, the only difference between `@ContextConfiguration`
|
|
|
|
|
and `@SpringJUnitWebConfig` is that you can declare component classes by using the
|
|
|
|
|
`value` attribute in `@SpringJUnitWebConfig`. In addition, you can override the `value`
|
|
|
|
|
attribute from `@WebAppConfiguration` only by using the `resourcePath` attribute in
|
|
|
|
|
`@SpringJUnitWebConfig`.
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@SpringJUnitWebConfig` annotation to specify
|
|
|
|
|
a configuration class:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitWebConfig(TestConfig.class) // <1>
|
|
|
|
|
class ConfigurationClassJUnitJupiterSpringWebTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the configuration class.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitWebConfig(TestConfig::class) // <1>
|
|
|
|
|
class ConfigurationClassJUnitJupiterSpringWebTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the configuration class.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following example shows how to use the `@SpringJUnitWebConfig` annotation to specify the
|
|
|
|
|
location of a configuration file:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitWebConfig(locations = "/test-config.xml") // <1>
|
|
|
|
|
class XmlJUnitJupiterSpringWebTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the location of a configuration file.
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@SpringJUnitWebConfig(locations = ["/test-config.xml"]) // <1>
|
|
|
|
|
class XmlJUnitJupiterSpringWebTests {
|
|
|
|
|
// class body...
|
|
|
|
|
}
|
|
|
|
|
----
|
|
|
|
|
<1> Specify the location of a configuration file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
See <<testcontext-ctx-management>> as well as the javadoc for
|
|
|
|
|
{api-spring-framework}/test/context/junit/jupiter/web/SpringJUnitWebConfig.html[`@SpringJUnitWebConfig`],
|
|
|
|
|
{api-spring-framework}/test/context/ContextConfiguration.html[`@ContextConfiguration`], and
|
|
|
|
|
{api-spring-framework}/test/context/web/WebAppConfiguration.html[`@WebAppConfiguration`]
|
|
|
|
|
for further details.
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-testconstructor]]
|
|
|
|
|
=== `@TestConstructor`
|
|
|
|
|
|
|
|
|
|
`@TestConstructor` is a type-level annotation that is used to configure how the parameters
|
|
|
|
|
of a test class constructor are autowired from components in the test's
|
|
|
|
|
`ApplicationContext`.
|
|
|
|
|
|
|
|
|
|
If `@TestConstructor` is not present or meta-present on a test class, the default _test
|
|
|
|
|
constructor autowire mode_ will be used. See the tip below for details on how to change
|
|
|
|
|
the default mode. Note, however, that a local declaration of `@Autowired` on a
|
|
|
|
|
constructor takes precedence over both `@TestConstructor` and the default mode.
|
|
|
|
|
|
|
|
|
|
.Changing the default test constructor autowire mode
|
|
|
|
|
[TIP]
|
|
|
|
|
=====
|
|
|
|
|
The default _test constructor autowire mode_ can be changed by setting the
|
|
|
|
|
`spring.test.constructor.autowire.mode` JVM system property to `all`. Alternatively, the
|
|
|
|
|
default mode may be set via the
|
|
|
|
|
<<appendix.adoc#appendix-spring-properties,`SpringProperties`>> mechanism.
|
|
|
|
|
|
|
|
|
|
As of Spring Framework 5.3, the default mode may also be configured as a
|
|
|
|
|
https://junit.org/junit5/docs/current/user-guide/#running-tests-config-params[JUnit Platform configuration parameter].
|
|
|
|
|
|
|
|
|
|
If the `spring.test.constructor.autowire.mode` property is not set, test class
|
|
|
|
|
constructors will not be automatically autowired.
|
|
|
|
|
=====
|
|
|
|
|
|
|
|
|
|
NOTE: As of Spring Framework 5.2, `@TestConstructor` is only supported in conjunction
|
|
|
|
|
with the `SpringExtension` for use with JUnit Jupiter. Note that the `SpringExtension` is
|
|
|
|
|
often automatically registered for you – for example, when using annotations such as
|
|
|
|
|
`@SpringJUnitConfig` and `@SpringJUnitWebConfig` or various test-related annotations from
|
|
|
|
|
Spring Boot Test.
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-nestedtestconfiguration]]
|
|
|
|
|
=== `@NestedTestConfiguration`
|
|
|
|
|
|
|
|
|
|
`@NestedTestConfiguration` is a type-level annotation that is used to configure how
|
|
|
|
|
Spring test configuration annotations are processed within enclosing class hierarchies
|
|
|
|
|
for inner test classes.
|
|
|
|
|
|
|
|
|
|
If `@NestedTestConfiguration` is not present or meta-present on a test class, in its
|
|
|
|
|
supertype hierarchy, or in its enclosing class hierarchy, the default _enclosing
|
|
|
|
|
configuration inheritance mode_ will be used. See the tip below for details on how to
|
|
|
|
|
change the default mode.
|
|
|
|
|
|
|
|
|
|
.Changing the default enclosing configuration inheritance mode
|
|
|
|
|
[TIP]
|
|
|
|
|
=====
|
|
|
|
|
The default _enclosing configuration inheritance mode_ is `INHERIT`, but it can be
|
|
|
|
|
changed by setting the `spring.test.enclosing.configuration` JVM system property to
|
|
|
|
|
`OVERRIDE`. Alternatively, the default mode may be set via the
|
|
|
|
|
<<appendix.adoc#appendix-spring-properties,`SpringProperties`>> mechanism.
|
|
|
|
|
=====
|
|
|
|
|
|
|
|
|
|
The <<testcontext-framework>> honors `@NestedTestConfiguration` semantics for the
|
|
|
|
|
following annotations.
|
|
|
|
|
|
|
|
|
|
* <<spring-testing-annotation-bootstrapwith>>
|
|
|
|
|
* <<spring-testing-annotation-contextconfiguration>>
|
|
|
|
|
* <<spring-testing-annotation-webappconfiguration>>
|
|
|
|
|
* <<spring-testing-annotation-contexthierarchy>>
|
|
|
|
|
* <<spring-testing-annotation-activeprofiles>>
|
|
|
|
|
* <<spring-testing-annotation-testpropertysource>>
|
|
|
|
|
* <<spring-testing-annotation-dynamicpropertysource>>
|
|
|
|
|
* <<spring-testing-annotation-dirtiescontext>>
|
|
|
|
|
* <<spring-testing-annotation-testexecutionlisteners>>
|
|
|
|
|
* <<spring-testing-annotation-recordapplicationevents>>
|
|
|
|
|
* <<testcontext-tx,`@Transactional`>>
|
|
|
|
|
* <<spring-testing-annotation-commit>>
|
|
|
|
|
* <<spring-testing-annotation-rollback>>
|
|
|
|
|
* <<spring-testing-annotation-sql>>
|
|
|
|
|
* <<spring-testing-annotation-sqlconfig>>
|
|
|
|
|
* <<spring-testing-annotation-sqlmergemode>>
|
|
|
|
|
* <<integration-testing-annotations-testconstructor>>
|
|
|
|
|
|
|
|
|
|
NOTE: The use of `@NestedTestConfiguration` typically only makes sense in conjunction
|
|
|
|
|
with `@Nested` test classes in JUnit Jupiter; however, there may be other testing
|
|
|
|
|
frameworks with support for Spring and nested test classes that make use of this
|
|
|
|
|
annotation.
|
|
|
|
|
|
|
|
|
|
See <<testcontext-junit-jupiter-nested-test-configuration>> for an example and further
|
|
|
|
|
details.
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit-jupiter-enabledif]]
|
|
|
|
|
=== `@EnabledIf`
|
|
|
|
|
|
|
|
|
|
`@EnabledIf` is used to signal that the annotated JUnit Jupiter test class or test method
|
|
|
|
|
is enabled and should be run if the supplied `expression` evaluates to `true`.
|
|
|
|
|
Specifically, if the expression evaluates to `Boolean.TRUE` or a `String` equal to `true`
|
|
|
|
|
(ignoring case), the test is enabled. When applied at the class level, all test methods
|
|
|
|
|
within that class are automatically enabled by default as well.
|
|
|
|
|
|
|
|
|
|
Expressions can be any of the following:
|
|
|
|
|
|
|
|
|
|
* <<core.adoc#expressions, Spring Expression Language>> (SpEL) expression. For example:
|
|
|
|
|
`@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")`
|
|
|
|
|
* Placeholder for a property available in the Spring <<core.adoc#beans-environment, `Environment`>>.
|
|
|
|
|
For example: `@EnabledIf("${smoke.tests.enabled}")`
|
|
|
|
|
* Text literal. For example: `@EnabledIf("true")`
|
|
|
|
|
|
|
|
|
|
Note, however, that a text literal that is not the result of dynamic resolution of a
|
|
|
|
|
property placeholder is of zero practical value, since `@EnabledIf("false")` is
|
|
|
|
|
equivalent to `@Disabled` and `@EnabledIf("true")` is logically meaningless.
|
|
|
|
|
|
|
|
|
|
You can use `@EnabledIf` as a meta-annotation to create custom composed annotations. For
|
|
|
|
|
example, you can create a custom `@EnabledOnMac` annotation as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@EnabledIf(
|
|
|
|
|
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
|
|
|
|
reason = "Enabled on Mac OS"
|
|
|
|
|
)
|
|
|
|
|
public @interface EnabledOnMac {}
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
|
|
|
|
|
@Retention(AnnotationRetention.RUNTIME)
|
|
|
|
|
@EnabledIf(
|
|
|
|
|
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
|
|
|
|
reason = "Enabled on Mac OS"
|
|
|
|
|
)
|
|
|
|
|
annotation class EnabledOnMac {}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
`@EnabledOnMac` is meant only as an example of what is possible. If you have that exact
|
|
|
|
|
use case, please use the built-in `@EnabledOnOs(MAC)` support in JUnit Jupiter.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[WARNING]
|
|
|
|
|
====
|
|
|
|
|
Since JUnit 5.7, JUnit Jupiter also has a condition annotation named `@EnabledIf`. Thus,
|
|
|
|
|
if you wish to use Spring's `@EnabledIf` support make sure you import the annotation type
|
|
|
|
|
from the correct package.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-junit-jupiter-disabledif]]
|
|
|
|
|
=== `@DisabledIf`
|
|
|
|
|
|
|
|
|
|
`@DisabledIf` is used to signal that the annotated JUnit Jupiter test class or test
|
|
|
|
|
method is disabled and should not be run if the supplied `expression` evaluates to
|
|
|
|
|
`true`. Specifically, if the expression evaluates to `Boolean.TRUE` or a `String` equal
|
|
|
|
|
to `true` (ignoring case), the test is disabled. When applied at the class level, all
|
|
|
|
|
test methods within that class are automatically disabled as well.
|
|
|
|
|
|
|
|
|
|
Expressions can be any of the following:
|
|
|
|
|
|
|
|
|
|
* <<core.adoc#expressions, Spring Expression Language>> (SpEL) expression. For example:
|
|
|
|
|
`@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")`
|
|
|
|
|
* Placeholder for a property available in the Spring <<core.adoc#beans-environment, `Environment`>>.
|
|
|
|
|
For example: `@DisabledIf("${smoke.tests.disabled}")`
|
|
|
|
|
* Text literal. For example: `@DisabledIf("true")`
|
|
|
|
|
|
|
|
|
|
Note, however, that a text literal that is not the result of dynamic resolution of a
|
|
|
|
|
property placeholder is of zero practical value, since `@DisabledIf("true")` is
|
|
|
|
|
equivalent to `@Disabled` and `@DisabledIf("false")` is logically meaningless.
|
|
|
|
|
|
|
|
|
|
You can use `@DisabledIf` as a meta-annotation to create custom composed annotations. For
|
|
|
|
|
example, you can create a custom `@DisabledOnMac` annotation as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@DisabledIf(
|
|
|
|
|
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
|
|
|
|
reason = "Disabled on Mac OS"
|
|
|
|
|
)
|
|
|
|
|
public @interface DisabledOnMac {}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
|
|
|
|
|
@Retention(AnnotationRetention.RUNTIME)
|
|
|
|
|
@DisabledIf(
|
|
|
|
|
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
|
|
|
|
reason = "Disabled on Mac OS"
|
|
|
|
|
)
|
|
|
|
|
annotation class DisabledOnMac {}
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[NOTE]
|
|
|
|
|
====
|
|
|
|
|
`@DisabledOnMac` is meant only as an example of what is possible. If you have that exact
|
|
|
|
|
use case, please use the built-in `@DisabledOnOs(MAC)` support in JUnit Jupiter.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
[WARNING]
|
|
|
|
|
====
|
|
|
|
|
Since JUnit 5.7, JUnit Jupiter also has a condition annotation named `@DisabledIf`. Thus,
|
|
|
|
|
if you wish to use Spring's `@DisabledIf` support make sure you import the annotation type
|
|
|
|
|
from the correct package.
|
|
|
|
|
====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[integration-testing-annotations-meta]]
|
|
|
|
|
== Meta-Annotation Support for Testing
|
|
|
|
|
|
|
|
|
|
You can use most test-related annotations as
|
|
|
|
|
<<core.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
|
|
|
|
|
<<testcontext-framework, 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:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@Target(ElementType.TYPE)
|
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
|
|
|
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
|
|
|
|
|
@ActiveProfiles("dev")
|
|
|
|
|
@Transactional
|
|
|
|
|
public @interface TransactionalDevTestConfig { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@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:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@RunWith(SpringRunner.class)
|
|
|
|
|
@TransactionalDevTestConfig
|
|
|
|
|
public class OrderRepositoryTests { }
|
|
|
|
|
|
|
|
|
|
@RunWith(SpringRunner.class)
|
|
|
|
|
@TransactionalDevTestConfig
|
|
|
|
|
public class UserRepositoryTests { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@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 5 can also be used as meta-annotations. Consider the following
|
|
|
|
|
example:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Then we can use our custom `@TransactionalDevTestConfig` annotation to simplify the
|
|
|
|
|
configuration of individual JUnit Jupiter based test classes, as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@TransactionalDevTestConfig
|
|
|
|
|
class OrderRepositoryTests { }
|
|
|
|
|
|
|
|
|
|
@TransactionalDevTestConfig
|
|
|
|
|
class UserRepositoryTests { }
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@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:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.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 { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
Then we can use our custom `@TransactionalIntegrationTest` annotation to simplify the
|
|
|
|
|
configuration of individual JUnit Jupiter based test methods, as follows:
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
|
|
|
|
.Java
|
|
|
|
|
----
|
|
|
|
|
@TransactionalIntegrationTest
|
|
|
|
|
void saveOrder() { }
|
|
|
|
|
|
|
|
|
|
@TransactionalIntegrationTest
|
|
|
|
|
void deleteOrder() { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
|
|
|
.Kotlin
|
|
|
|
|
----
|
|
|
|
|
@TransactionalIntegrationTest
|
|
|
|
|
fun saveOrder() { }
|
|
|
|
|
|
|
|
|
|
@TransactionalIntegrationTest
|
|
|
|
|
fun deleteOrder() { }
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
For further details, see the
|
|
|
|
|
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model[Spring Annotation Programming Model]
|
|
|
|
|
wiki page.
|