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.
4725 lines
179 KiB
4725 lines
179 KiB
[[testcontext-framework]] |
|
= Spring TestContext Framework |
|
|
|
The Spring TestContext Framework (located in the `org.springframework.test.context` |
|
package) provides generic, annotation-driven unit and integration testing support that is |
|
agnostic of the testing framework in use. The TestContext framework also places a great |
|
deal of importance on convention over configuration, with reasonable defaults that you |
|
can override through annotation-based configuration. |
|
|
|
In addition to generic testing infrastructure, the TestContext framework provides |
|
explicit support for JUnit 4, JUnit Jupiter (AKA JUnit 5), and TestNG. For JUnit 4 and |
|
TestNG, Spring provides `abstract` support classes. Furthermore, Spring provides a custom |
|
JUnit `Runner` and custom JUnit `Rules` for JUnit 4 and a custom `Extension` for JUnit |
|
Jupiter that let you write so-called POJO test classes. POJO test classes are not |
|
required to extend a particular class hierarchy, such as the `abstract` support classes. |
|
|
|
The following section provides an overview of the internals of the TestContext framework. |
|
If you are interested only in using the framework and are not interested in extending it |
|
with your own custom listeners or custom loaders, feel free to go directly to the |
|
configuration (<<testcontext-ctx-management, context management>>, |
|
<<testcontext-fixture-di, dependency injection>>, <<testcontext-tx,transaction |
|
management>>), <<testcontext-support-classes, support classes>>, and |
|
<<integration-testing-annotations, annotation support>> sections. |
|
|
|
|
|
[[testcontext-key-abstractions]] |
|
== Key Abstractions |
|
|
|
The core of the framework consists of the `TestContextManager` class and the |
|
`TestContext`, `TestExecutionListener`, and `SmartContextLoader` interfaces. A |
|
`TestContextManager` is created for each test class (for example, for the execution of |
|
all test methods within a single test class in JUnit Jupiter). The `TestContextManager`, |
|
in turn, manages a `TestContext` that holds the context of the current test. The |
|
`TestContextManager` also updates the state of the `TestContext` as the test progresses |
|
and delegates to `TestExecutionListener` implementations, which instrument the actual |
|
test execution by providing dependency injection, managing transactions, and so on. A |
|
`SmartContextLoader` is responsible for loading an `ApplicationContext` for a given test |
|
class. See the {api-spring-framework}/test/context/package-summary.html[javadoc] and the |
|
Spring test suite for further information and examples of various implementations. |
|
|
|
=== `TestContext` |
|
|
|
`TestContext` encapsulates the context in which a test is run (agnostic of the |
|
actual testing framework in use) and provides context management and caching support for |
|
the test instance for which it is responsible. The `TestContext` also delegates to a |
|
`SmartContextLoader` to load an `ApplicationContext` if requested. |
|
|
|
=== `TestContextManager` |
|
|
|
`TestContextManager` is the main entry point into the Spring TestContext Framework and is |
|
responsible for managing a single `TestContext` and signaling events to each registered |
|
`TestExecutionListener` at well-defined test execution points: |
|
|
|
* Prior to any "`before class`" or "`before all`" methods of a particular testing framework. |
|
* Test instance post-processing. |
|
* Prior to any "`before`" or "`before each`" methods of a particular testing framework. |
|
* Immediately before execution of the test method but after test setup. |
|
* Immediately after execution of the test method but before test tear down. |
|
* After any "`after`" or "`after each`" methods of a particular testing framework. |
|
* After any "`after class`" or "`after all`" methods of a particular testing framework. |
|
|
|
=== `TestExecutionListener` |
|
|
|
`TestExecutionListener` defines the API for reacting to test-execution events published by |
|
the `TestContextManager` with which the listener is registered. See <<testcontext-tel-config>>. |
|
|
|
=== Context Loaders |
|
|
|
`ContextLoader` is a strategy interface for loading an `ApplicationContext` for an |
|
integration test managed by the Spring TestContext Framework. You should implement |
|
`SmartContextLoader` instead of this interface to provide support for component classes, |
|
active bean definition profiles, test property sources, context hierarchies, and |
|
`WebApplicationContext` support. |
|
|
|
`SmartContextLoader` is an extension of the `ContextLoader` interface that supersedes the |
|
original minimal `ContextLoader` SPI. Specifically, a `SmartContextLoader` can choose to |
|
process resource locations, component classes, or context initializers. Furthermore, a |
|
`SmartContextLoader` can set active bean definition profiles and test property sources in |
|
the context that it loads. |
|
|
|
Spring provides the following implementations: |
|
|
|
* `DelegatingSmartContextLoader`: One of two default loaders, it delegates internally to |
|
an `AnnotationConfigContextLoader`, a `GenericXmlContextLoader`, or a |
|
`GenericGroovyXmlContextLoader`, depending either on the configuration declared for the |
|
test class or on the presence of default locations or default configuration classes. |
|
Groovy support is enabled only if Groovy is on the classpath. |
|
* `WebDelegatingSmartContextLoader`: One of two default loaders, it delegates internally |
|
to an `AnnotationConfigWebContextLoader`, a `GenericXmlWebContextLoader`, or a |
|
`GenericGroovyXmlWebContextLoader`, depending either on the configuration declared for |
|
the test class or on the presence of default locations or default configuration |
|
classes. A web `ContextLoader` is used only if `@WebAppConfiguration` is present on the |
|
test class. Groovy support is enabled only if Groovy is on the classpath. |
|
* `AnnotationConfigContextLoader`: Loads a standard `ApplicationContext` from component |
|
classes. |
|
* `AnnotationConfigWebContextLoader`: Loads a `WebApplicationContext` from component |
|
classes. |
|
* `GenericGroovyXmlContextLoader`: Loads a standard `ApplicationContext` from resource |
|
locations that are either Groovy scripts or XML configuration files. |
|
* `GenericGroovyXmlWebContextLoader`: Loads a `WebApplicationContext` from resource |
|
locations that are either Groovy scripts or XML configuration files. |
|
* `GenericXmlContextLoader`: Loads a standard `ApplicationContext` from XML resource |
|
locations. |
|
* `GenericXmlWebContextLoader`: Loads a `WebApplicationContext` from XML resource |
|
locations. |
|
|
|
|
|
[[testcontext-bootstrapping]] |
|
== Bootstrapping the TestContext Framework |
|
|
|
The default configuration for the internals of the Spring TestContext Framework is |
|
sufficient for all common use cases. However, there are times when a development team or |
|
third party framework would like to change the default `ContextLoader`, implement a |
|
custom `TestContext` or `ContextCache`, augment the default sets of |
|
`ContextCustomizerFactory` and `TestExecutionListener` implementations, and so on. For |
|
such low-level control over how the TestContext framework operates, Spring provides a |
|
bootstrapping strategy. |
|
|
|
`TestContextBootstrapper` defines the SPI for bootstrapping the TestContext framework. A |
|
`TestContextBootstrapper` is used by the `TestContextManager` to load the |
|
`TestExecutionListener` implementations for the current test and to build the |
|
`TestContext` that it manages. You can configure a custom bootstrapping strategy for a |
|
test class (or test class hierarchy) by using `@BootstrapWith`, either directly or as a |
|
meta-annotation. If a bootstrapper is not explicitly configured by using |
|
`@BootstrapWith`, either the `DefaultTestContextBootstrapper` or the |
|
`WebTestContextBootstrapper` is used, depending on the presence of `@WebAppConfiguration`. |
|
|
|
Since the `TestContextBootstrapper` SPI is likely to change in the future (to accommodate |
|
new requirements), we strongly encourage implementers not to implement this interface |
|
directly but rather to extend `AbstractTestContextBootstrapper` or one of its concrete |
|
subclasses instead. |
|
|
|
|
|
[[testcontext-tel-config]] |
|
== `TestExecutionListener` Configuration |
|
|
|
Spring provides the following `TestExecutionListener` implementations that are registered |
|
by default, exactly in the following order: |
|
|
|
* `ServletTestExecutionListener`: Configures Servlet API mocks for a |
|
`WebApplicationContext`. |
|
* `DirtiesContextBeforeModesTestExecutionListener`: Handles the `@DirtiesContext` |
|
annotation for "`before`" modes. |
|
* `ApplicationEventsTestExecutionListener`: Provides support for |
|
<<testcontext-application-events, `ApplicationEvents`>>. |
|
* `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test |
|
instance. |
|
* `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for |
|
"`after`" modes. |
|
* `TransactionalTestExecutionListener`: Provides transactional test execution with |
|
default rollback semantics. |
|
* `SqlScriptsTestExecutionListener`: Runs SQL scripts configured by using the `@Sql` |
|
annotation. |
|
* `EventPublishingTestExecutionListener`: Publishes test execution events to the test's |
|
`ApplicationContext` (see <<testcontext-test-execution-events>>). |
|
|
|
[[testcontext-tel-config-registering-tels]] |
|
=== Registering `TestExecutionListener` Implementations |
|
|
|
You can register `TestExecutionListener` implementations explicitly for a test class, its |
|
subclasses, and its nested classes by using the `@TestExecutionListeners` annotation. See |
|
<<integration-testing-annotations, annotation support>> and the javadoc for |
|
{api-spring-framework}/test/context/TestExecutionListeners.html[`@TestExecutionListeners`] |
|
for details and examples. |
|
|
|
.Switching to default `TestExecutionListener` implementations |
|
[NOTE] |
|
==== |
|
If you extend a class that is annotated with `@TestExecutionListeners` and you need to |
|
switch to using the default set of listeners, you can annotate your class with the |
|
following. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Switch to default listeners |
|
@TestExecutionListeners( |
|
listeners = {}, |
|
inheritListeners = false, |
|
mergeMode = MERGE_WITH_DEFAULTS) |
|
class MyTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Switch to default listeners |
|
@TestExecutionListeners( |
|
listeners = [], |
|
inheritListeners = false, |
|
mergeMode = MERGE_WITH_DEFAULTS) |
|
class MyTest : BaseTest { |
|
// class body... |
|
} |
|
---- |
|
==== |
|
|
|
[[testcontext-tel-config-automatic-discovery]] |
|
=== Automatic Discovery of Default `TestExecutionListener` Implementations |
|
|
|
Registering `TestExecutionListener` implementations by using `@TestExecutionListeners` is |
|
suitable for custom listeners that are used in limited testing scenarios. However, it can |
|
become cumbersome if a custom listener needs to be used across an entire test suite. This |
|
issue is addressed through support for automatic discovery of default |
|
`TestExecutionListener` implementations through the `SpringFactoriesLoader` mechanism. |
|
|
|
Specifically, the `spring-test` module declares all core default `TestExecutionListener` |
|
implementations under the `org.springframework.test.context.TestExecutionListener` key in |
|
its `META-INF/spring.factories` properties file. Third-party frameworks and developers |
|
can contribute their own `TestExecutionListener` implementations to the list of default |
|
listeners in the same manner through their own `META-INF/spring.factories` properties |
|
file. |
|
|
|
[[testcontext-tel-config-ordering]] |
|
=== Ordering `TestExecutionListener` Implementations |
|
|
|
When the TestContext framework discovers default `TestExecutionListener` implementations |
|
through the <<testcontext-tel-config-automatic-discovery, aforementioned>> |
|
`SpringFactoriesLoader` mechanism, the instantiated listeners are sorted by using |
|
Spring's `AnnotationAwareOrderComparator`, which honors Spring's `Ordered` interface and |
|
`@Order` annotation for ordering. `AbstractTestExecutionListener` and all default |
|
`TestExecutionListener` implementations provided by Spring implement `Ordered` with |
|
appropriate values. Third-party frameworks and developers should therefore make sure that |
|
their default `TestExecutionListener` implementations are registered in the proper order |
|
by implementing `Ordered` or declaring `@Order`. See the javadoc for the `getOrder()` |
|
methods of the core default `TestExecutionListener` implementations for details on what |
|
values are assigned to each core listener. |
|
|
|
[[testcontext-tel-config-merging]] |
|
=== Merging `TestExecutionListener` Implementations |
|
|
|
If a custom `TestExecutionListener` is registered via `@TestExecutionListeners`, the |
|
default listeners are not registered. In most common testing scenarios, this effectively |
|
forces the developer to manually declare all default listeners in addition to any custom |
|
listeners. The following listing demonstrates this style of configuration: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ContextConfiguration |
|
@TestExecutionListeners({ |
|
MyCustomTestExecutionListener.class, |
|
ServletTestExecutionListener.class, |
|
DirtiesContextBeforeModesTestExecutionListener.class, |
|
DependencyInjectionTestExecutionListener.class, |
|
DirtiesContextTestExecutionListener.class, |
|
TransactionalTestExecutionListener.class, |
|
SqlScriptsTestExecutionListener.class |
|
}) |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ContextConfiguration |
|
@TestExecutionListeners( |
|
MyCustomTestExecutionListener::class, |
|
ServletTestExecutionListener::class, |
|
DirtiesContextBeforeModesTestExecutionListener::class, |
|
DependencyInjectionTestExecutionListener::class, |
|
DirtiesContextTestExecutionListener::class, |
|
TransactionalTestExecutionListener::class, |
|
SqlScriptsTestExecutionListener::class |
|
) |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
The challenge with this approach is that it requires that the developer know exactly |
|
which listeners are registered by default. Moreover, the set of default listeners can |
|
change from release to release -- for example, `SqlScriptsTestExecutionListener` was |
|
introduced in Spring Framework 4.1, and `DirtiesContextBeforeModesTestExecutionListener` |
|
was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring |
|
Boot and Spring Security register their own default `TestExecutionListener` |
|
implementations by using the aforementioned <<testcontext-tel-config-automatic-discovery, |
|
automatic discovery mechanism>>. |
|
|
|
To avoid having to be aware of and re-declare all default listeners, you can set the |
|
`mergeMode` attribute of `@TestExecutionListeners` to `MergeMode.MERGE_WITH_DEFAULTS`. |
|
`MERGE_WITH_DEFAULTS` indicates that locally declared listeners should be merged with the |
|
default listeners. The merging algorithm ensures that duplicates are removed from the |
|
list and that the resulting set of merged listeners is sorted according to the semantics |
|
of `AnnotationAwareOrderComparator`, as described in <<testcontext-tel-config-ordering>>. |
|
If a listener implements `Ordered` or is annotated with `@Order`, it can influence the |
|
position in which it is merged with the defaults. Otherwise, locally declared listeners |
|
are appended to the list of default listeners when merged. |
|
|
|
For example, if the `MyCustomTestExecutionListener` class in the previous example |
|
configures its `order` value (for example, `500`) to be less than the order of the |
|
`ServletTestExecutionListener` (which happens to be `1000`), the |
|
`MyCustomTestExecutionListener` can then be automatically merged with the list of |
|
defaults in front of the `ServletTestExecutionListener`, and the previous example could |
|
be replaced with the following: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ContextConfiguration |
|
@TestExecutionListeners( |
|
listeners = MyCustomTestExecutionListener.class, |
|
mergeMode = MERGE_WITH_DEFAULTS |
|
) |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ContextConfiguration |
|
@TestExecutionListeners( |
|
listeners = [MyCustomTestExecutionListener::class], |
|
mergeMode = MERGE_WITH_DEFAULTS |
|
) |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
|
|
[[testcontext-application-events]] |
|
== Application Events |
|
|
|
Since Spring Framework 5.3.3, the TestContext framework provides support for recording |
|
<<core.adoc#context-functionality-events, application events>> published in the |
|
`ApplicationContext` so that assertions can be performed against those events within |
|
tests. All events published during the execution of a single test are made available via |
|
the `ApplicationEvents` API which allows you to process the events as a |
|
`java.util.Stream`. |
|
|
|
To use `ApplicationEvents` in your tests, do the following. |
|
|
|
* Ensure that your test class is annotated or meta-annotated with |
|
<<spring-testing-annotation-recordapplicationevents>>. |
|
* Ensure that the `ApplicationEventsTestExecutionListener` is registered. Note, however, |
|
that `ApplicationEventsTestExecutionListener` is registered by default and only needs |
|
to be manually registered if you have custom configuration via |
|
`@TestExecutionListeners` that does not include the default listeners. |
|
* Annotate a field of type `ApplicationEvents` with `@Autowired` and use that instance of |
|
`ApplicationEvents` in your test and lifecycle methods (such as `@BeforeEach` and |
|
`@AfterEach` methods in JUnit Jupiter). |
|
** When using the <<testcontext-junit-jupiter-extension>>, you may declare a method |
|
parameter of type `ApplicationEvents` in a test or lifecycle method as an alternative |
|
to an `@Autowired` field in the test class. |
|
|
|
The following test class uses the `SpringExtension` for JUnit Jupiter and |
|
https://assertj.github.io/doc/[AssertJ] to assert the types of application events |
|
published while invoking a method in a Spring-managed component: |
|
|
|
// Don't use "quotes" in the "subs" section because of the asterisks in /* ... */ |
|
[source,java,indent=0,subs="verbatim",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(/* ... */) |
|
@RecordApplicationEvents // <1> |
|
class OrderServiceTests { |
|
|
|
@Autowired |
|
OrderService orderService; |
|
|
|
@Autowired |
|
ApplicationEvents events; // <2> |
|
|
|
@Test |
|
void submitOrder() { |
|
// Invoke method in OrderService that publishes an event |
|
orderService.submitOrder(new Order(/* ... */)); |
|
// Verify that an OrderSubmitted event was published |
|
long numEvents = events.stream(OrderSubmitted.class).count(); // <3> |
|
assertThat(numEvents).isEqualTo(1); |
|
} |
|
} |
|
---- |
|
<1> Annotate the test class with `@RecordApplicationEvents`. |
|
<2> Inject the `ApplicationEvents` instance for the current test. |
|
<3> Use the `ApplicationEvents` API to count how many `OrderSubmitted` events were published. |
|
|
|
// Don't use "quotes" in the "subs" section because of the asterisks in /* ... */ |
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(/* ... */) |
|
@RecordApplicationEvents // <1> |
|
class OrderServiceTests { |
|
|
|
@Autowired |
|
lateinit var orderService: OrderService |
|
|
|
@Autowired |
|
lateinit var events: ApplicationEvents // <2> |
|
|
|
@Test |
|
fun submitOrder() { |
|
// Invoke method in OrderService that publishes an event |
|
orderService.submitOrder(Order(/* ... */)) |
|
// Verify that an OrderSubmitted event was published |
|
val numEvents = events.stream(OrderSubmitted::class).count() // <3> |
|
assertThat(numEvents).isEqualTo(1) |
|
} |
|
} |
|
---- |
|
<1> Annotate the test class with `@RecordApplicationEvents`. |
|
<2> Inject the `ApplicationEvents` instance for the current test. |
|
<3> Use the `ApplicationEvents` API to count how many `OrderSubmitted` events were published. |
|
|
|
See the |
|
{api-spring-framework}/test/context/event/ApplicationEvents.html[`ApplicationEvents` |
|
javadoc] for further details regarding the `ApplicationEvents` API. |
|
|
|
[[testcontext-test-execution-events]] |
|
== Test Execution Events |
|
|
|
The `EventPublishingTestExecutionListener` introduced in Spring Framework 5.2 offers an |
|
alternative approach to implementing a custom `TestExecutionListener`. Components in the |
|
test's `ApplicationContext` can listen to the following events published by the |
|
`EventPublishingTestExecutionListener`, each of which corresponds to a method in the |
|
`TestExecutionListener` API. |
|
|
|
* `BeforeTestClassEvent` |
|
* `PrepareTestInstanceEvent` |
|
* `BeforeTestMethodEvent` |
|
* `BeforeTestExecutionEvent` |
|
* `AfterTestExecutionEvent` |
|
* `AfterTestMethodEvent` |
|
* `AfterTestClassEvent` |
|
|
|
These events may be consumed for various reasons, such as resetting mock beans or tracing |
|
test execution. One advantage of consuming test execution events rather than implementing |
|
a custom `TestExecutionListener` is that test execution events may be consumed by any |
|
Spring bean registered in the test `ApplicationContext`, and such beans may benefit |
|
directly from dependency injection and other features of the `ApplicationContext`. In |
|
contrast, a `TestExecutionListener` is not a bean in the `ApplicationContext`. |
|
|
|
[NOTE] |
|
==== |
|
The `EventPublishingTestExecutionListener` is registered by default; however, it only |
|
publishes events if the `ApplicationContext` has _already been loaded_. This prevents the |
|
`ApplicationContext` from being loaded unnecessarily or too early. |
|
|
|
Consequently, a `BeforeTestClassEvent` will not be published until after the |
|
`ApplicationContext` has been loaded by another `TestExecutionListener`. For example, with |
|
the default set of `TestExecutionListener` implementations registered, a |
|
`BeforeTestClassEvent` will not be published for the first test class that uses a |
|
particular test `ApplicationContext`, but a `BeforeTestClassEvent` _will_ be published for |
|
any subsequent test class in the same test suite that uses the same test |
|
`ApplicationContext` since the context will already have been loaded when subsequent test |
|
classes run (as long as the context has not been removed from the `ContextCache` via |
|
`@DirtiesContext` or the max-size eviction policy). |
|
|
|
If you wish to ensure that a `BeforeTestClassEvent` is always published for every test |
|
class, you need to register a `TestExecutionListener` that loads the `ApplicationContext` |
|
in the `beforeTestClass` callback, and that `TestExecutionListener` must be registered |
|
_before_ the `EventPublishingTestExecutionListener`. |
|
|
|
Similarly, if `@DirtiesContext` is used to remove the `ApplicationContext` from the |
|
context cache after the last test method in a given test class, the `AfterTestClassEvent` |
|
will not be published for that test class. |
|
==== |
|
|
|
In order to listen to test execution events, a Spring bean may choose to implement the |
|
`org.springframework.context.ApplicationListener` interface. Alternatively, listener |
|
methods can be annotated with `@EventListener` and configured to listen to one of the |
|
particular event types listed above (see |
|
<<core.adoc#context-functionality-events-annotation, Annotation-based Event Listeners>>). |
|
Due to the popularity of this approach, Spring provides the following dedicated |
|
`@EventListener` annotations to simplify registration of test execution event listeners. |
|
These annotations reside in the `org.springframework.test.context.event.annotation` |
|
package. |
|
|
|
* `@BeforeTestClass` |
|
* `@PrepareTestInstance` |
|
* `@BeforeTestMethod` |
|
* `@BeforeTestExecution` |
|
* `@AfterTestExecution` |
|
* `@AfterTestMethod` |
|
* `@AfterTestClass` |
|
|
|
[[testcontext-test-execution-events-exception-handling]] |
|
=== Exception Handling |
|
|
|
By default, if a test execution event listener throws an exception while consuming an |
|
event, that exception will propagate to the underlying testing framework in use (such as |
|
JUnit or TestNG). For example, if the consumption of a `BeforeTestMethodEvent` results in |
|
an exception, the corresponding test method will fail as a result of the exception. In |
|
contrast, if an asynchronous test execution event listener throws an exception, the |
|
exception will not propagate to the underlying testing framework. For further details on |
|
asynchronous exception handling, consult the class-level javadoc for `@EventListener`. |
|
|
|
[[testcontext-test-execution-events-async]] |
|
=== Asynchronous Listeners |
|
|
|
If you want a particular test execution event listener to process events asynchronously, |
|
you can use Spring's <<integration.adoc#scheduling-annotation-support-async,regular |
|
`@Async` support>>. For further details, consult the class-level javadoc for |
|
`@EventListener`. |
|
|
|
|
|
[[testcontext-ctx-management]] |
|
== Context Management |
|
|
|
Each `TestContext` provides context management and caching support for the test instance |
|
for which it is responsible. Test instances do not automatically receive access to the |
|
configured `ApplicationContext`. However, if a test class implements the |
|
`ApplicationContextAware` interface, a reference to the `ApplicationContext` is supplied |
|
to the test instance. Note that `AbstractJUnit4SpringContextTests` and |
|
`AbstractTestNGSpringContextTests` implement `ApplicationContextAware` and, therefore, |
|
provide access to the `ApplicationContext` automatically. |
|
|
|
.@Autowired ApplicationContext |
|
[TIP] |
|
===== |
|
As an alternative to implementing the `ApplicationContextAware` interface, you can inject |
|
the application context for your test class through the `@Autowired` annotation on either |
|
a field or setter method, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig |
|
class MyTest { |
|
|
|
@Autowired // <1> |
|
ApplicationContext applicationContext; |
|
|
|
// class body... |
|
} |
|
---- |
|
<1> Injecting the `ApplicationContext`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig |
|
class MyTest { |
|
|
|
@Autowired // <1> |
|
lateinit var applicationContext: ApplicationContext |
|
|
|
// class body... |
|
} |
|
---- |
|
<1> Injecting the `ApplicationContext`. |
|
|
|
|
|
Similarly, if your test is configured to load a `WebApplicationContext`, you can inject |
|
the web application context into your test, as follows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitWebConfig // <1> |
|
class MyWebAppTest { |
|
|
|
@Autowired // <2> |
|
WebApplicationContext wac; |
|
|
|
// class body... |
|
} |
|
---- |
|
<1> Configuring the `WebApplicationContext`. |
|
<2> Injecting the `WebApplicationContext`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitWebConfig // <1> |
|
class MyWebAppTest { |
|
|
|
@Autowired // <2> |
|
lateinit var wac: WebApplicationContext |
|
// class body... |
|
} |
|
---- |
|
<1> Configuring the `WebApplicationContext`. |
|
<2> Injecting the `WebApplicationContext`. |
|
|
|
|
|
Dependency injection by using `@Autowired` is provided by the |
|
`DependencyInjectionTestExecutionListener`, which is configured by default |
|
(see <<testcontext-fixture-di>>). |
|
===== |
|
|
|
Test classes that use the TestContext framework do not need to extend any particular |
|
class or implement a specific interface to configure their application context. Instead, |
|
configuration is achieved by declaring the `@ContextConfiguration` annotation at the |
|
class level. If your test class does not explicitly declare application context resource |
|
locations or component classes, the configured `ContextLoader` determines how to load a |
|
context from a default location or default configuration classes. In addition to context |
|
resource locations and component classes, an application context can also be configured |
|
through application context initializers. |
|
|
|
The following sections explain how to use Spring's `@ContextConfiguration` annotation to |
|
configure a test `ApplicationContext` by using XML configuration files, Groovy scripts, |
|
component classes (typically `@Configuration` classes), or context initializers. |
|
Alternatively, you can implement and configure your own custom `SmartContextLoader` for |
|
advanced use cases. |
|
|
|
* <<testcontext-ctx-management-xml>> |
|
* <<testcontext-ctx-management-groovy>> |
|
* <<testcontext-ctx-management-javaconfig>> |
|
* <<testcontext-ctx-management-mixed-config>> |
|
* <<testcontext-ctx-management-initializers>> |
|
* <<testcontext-ctx-management-inheritance>> |
|
* <<testcontext-ctx-management-env-profiles>> |
|
* <<testcontext-ctx-management-property-sources>> |
|
* <<testcontext-ctx-management-dynamic-property-sources>> |
|
* <<testcontext-ctx-management-web>> |
|
* <<testcontext-ctx-management-caching>> |
|
* <<testcontext-ctx-management-ctx-hierarchies>> |
|
|
|
[[testcontext-ctx-management-xml]] |
|
=== Context Configuration with XML resources |
|
|
|
To load an `ApplicationContext` for your tests by using XML configuration files, annotate |
|
your test class with `@ContextConfiguration` and configure the `locations` attribute with |
|
an array that contains the resource locations of XML configuration metadata. A plain or |
|
relative path (for example, `context.xml`) is treated as a classpath resource that is |
|
relative to the package in which the test class is defined. A path starting with a slash |
|
is treated as an absolute classpath location (for example, `/org/example/config.xml`). A |
|
path that represents a resource URL (i.e., a path prefixed with `classpath:`, `file:`, |
|
`http:`, etc.) is used _as is_. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from "/app-config.xml" and |
|
// "/test-config.xml" in the root of the classpath |
|
@ContextConfiguration(locations = {"/app-config.xml", "/test-config.xml"}) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Setting the locations attribute to a list of XML files. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from "/app-config.xml" and |
|
// "/test-config.xml" in the root of the classpath |
|
@ContextConfiguration(locations = ["/app-config.xml", "/test-config.xml"]) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Setting the locations attribute to a list of XML files. |
|
|
|
|
|
`@ContextConfiguration` supports an alias for the `locations` attribute through the |
|
standard Java `value` attribute. Thus, if you do not need to declare additional |
|
attributes in `@ContextConfiguration`, you can omit the declaration of the `locations` |
|
attribute name and declare the resource locations by using the shorthand format |
|
demonstrated in the following example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
@ContextConfiguration({"/app-config.xml", "/test-config.xml"}) <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying XML files without using the `locations` attribute. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
@ContextConfiguration("/app-config.xml", "/test-config.xml") // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying XML files without using the `locations` attribute. |
|
|
|
|
|
If you omit both the `locations` and the `value` attributes from the |
|
`@ContextConfiguration` annotation, the TestContext framework tries to detect a default |
|
XML resource location. Specifically, `GenericXmlContextLoader` and |
|
`GenericXmlWebContextLoader` detect a default location based on the name of the test |
|
class. If your class is named `com.example.MyTest`, `GenericXmlContextLoader` loads your |
|
application context from `"classpath:com/example/MyTest-context.xml"`. The following |
|
example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from |
|
// "classpath:com/example/MyTest-context.xml" |
|
@ContextConfiguration // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Loading configuration from the default location. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from |
|
// "classpath:com/example/MyTest-context.xml" |
|
@ContextConfiguration // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Loading configuration from the default location. |
|
|
|
|
|
[[testcontext-ctx-management-groovy]] |
|
=== Context Configuration with Groovy Scripts |
|
|
|
To load an `ApplicationContext` for your tests by using Groovy scripts that use the |
|
<<core.adoc#groovy-bean-definition-dsl, Groovy Bean Definition DSL>>, you can annotate |
|
your test class with `@ContextConfiguration` and configure the `locations` or `value` |
|
attribute with an array that contains the resource locations of Groovy scripts. Resource |
|
lookup semantics for Groovy scripts are the same as those described for |
|
<<testcontext-ctx-management-xml, XML configuration files>>. |
|
|
|
.Enabling Groovy script support |
|
TIP: Support for using Groovy scripts to load an `ApplicationContext` in the Spring |
|
TestContext Framework is enabled automatically if Groovy is on the classpath. |
|
|
|
The following example shows how to specify Groovy configuration files: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from "/AppConfig.groovy" and |
|
// "/TestConfig.groovy" in the root of the classpath |
|
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"}) <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying the location of Groovy configuration files. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from "/AppConfig.groovy" and |
|
// "/TestConfig.groovy" in the root of the classpath |
|
@ContextConfiguration("/AppConfig.groovy", "/TestConfig.Groovy") // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying the location of Groovy configuration files. |
|
|
|
|
|
If you omit both the `locations` and `value` attributes from the `@ContextConfiguration` |
|
annotation, the TestContext framework tries to detect a default Groovy script. |
|
Specifically, `GenericGroovyXmlContextLoader` and `GenericGroovyXmlWebContextLoader` |
|
detect a default location based on the name of the test class. If your class is named |
|
`com.example.MyTest`, the Groovy context loader loads your application context from |
|
`"classpath:com/example/MyTestContext.groovy"`. The following example shows how to use |
|
the default: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from |
|
// "classpath:com/example/MyTestContext.groovy" |
|
@ContextConfiguration // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Loading configuration from the default location. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from |
|
// "classpath:com/example/MyTestContext.groovy" |
|
@ContextConfiguration // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Loading configuration from the default location. |
|
|
|
|
|
.Declaring XML configuration and Groovy scripts simultaneously |
|
[TIP] |
|
===== |
|
You can declare both XML configuration files and Groovy scripts simultaneously by using |
|
the `locations` or `value` attribute of `@ContextConfiguration`. If the path to a |
|
configured resource location ends with `.xml`, it is loaded by using an |
|
`XmlBeanDefinitionReader`. Otherwise, it is loaded by using a |
|
`GroovyBeanDefinitionReader`. |
|
|
|
The following listing shows how to combine both in an integration test: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from |
|
// "/app-config.xml" and "/TestConfig.groovy" |
|
@ContextConfiguration({ "/app-config.xml", "/TestConfig.groovy" }) |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from |
|
// "/app-config.xml" and "/TestConfig.groovy" |
|
@ContextConfiguration("/app-config.xml", "/TestConfig.groovy") |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
===== |
|
|
|
[[testcontext-ctx-management-javaconfig]] |
|
=== Context Configuration with Component Classes |
|
|
|
To load an `ApplicationContext` for your tests by using component classes (see |
|
<<core.adoc#beans-java, Java-based container configuration>>), you can annotate your test |
|
class with `@ContextConfiguration` and configure the `classes` attribute with an array |
|
that contains references to component classes. The following example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from AppConfig and TestConfig |
|
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class}) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying component classes. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from AppConfig and TestConfig |
|
@ContextConfiguration(classes = [AppConfig::class, TestConfig::class]) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying component classes. |
|
|
|
|
|
[[testcontext-ctx-management-javaconfig-component-classes]] |
|
.Component Classes |
|
[TIP] |
|
==== |
|
The term "`component class`" can refer to any of the following: |
|
|
|
* A class annotated with `@Configuration`. |
|
* A component (that is, a class annotated with `@Component`, `@Service`, `@Repository`, or other stereotype annotations). |
|
* A JSR-330 compliant class that is annotated with `jakarta.inject` annotations. |
|
* Any class that contains `@Bean`-methods. |
|
* Any other class that is intended to be registered as a Spring component (i.e., a Spring |
|
bean in the `ApplicationContext`), potentially taking advantage of automatic autowiring |
|
of a single constructor without the use of Spring annotations. |
|
|
|
See the javadoc of |
|
{api-spring-framework}/context/annotation/Configuration.html[`@Configuration`] and |
|
{api-spring-framework}/context/annotation/Bean.html[`@Bean`] for further information |
|
regarding the configuration and semantics of component classes, paying special attention |
|
to the discussion of `@Bean` Lite Mode. |
|
==== |
|
|
|
If you omit the `classes` attribute from the `@ContextConfiguration` annotation, the |
|
TestContext framework tries to detect the presence of default configuration classes. |
|
Specifically, `AnnotationConfigContextLoader` and `AnnotationConfigWebContextLoader` |
|
detect all `static` nested classes of the test class that meet the requirements for |
|
configuration class implementations, as specified in the |
|
{api-spring-framework}/context/annotation/Configuration.html[`@Configuration`] javadoc. |
|
Note that the name of the configuration class is arbitrary. In addition, a test class can |
|
contain more than one `static` nested configuration class if desired. In the following |
|
example, the `OrderServiceTest` class declares a `static` nested configuration class |
|
named `Config` that is automatically used to load the `ApplicationContext` for the test |
|
class: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig <1> |
|
// ApplicationContext will be loaded from the static nested Config class |
|
class OrderServiceTest { |
|
|
|
@Configuration |
|
static class Config { |
|
|
|
// this bean will be injected into the OrderServiceTest class |
|
@Bean |
|
OrderService orderService() { |
|
OrderService orderService = new OrderServiceImpl(); |
|
// set properties, etc. |
|
return orderService; |
|
} |
|
} |
|
|
|
@Autowired |
|
OrderService orderService; |
|
|
|
@Test |
|
void testOrderService() { |
|
// test the orderService |
|
} |
|
|
|
} |
|
---- |
|
<1> Loading configuration information from the nested `Config` class. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig <1> |
|
// ApplicationContext will be loaded from the nested Config class |
|
class OrderServiceTest { |
|
|
|
@Autowired |
|
lateinit var orderService: OrderService |
|
|
|
@Configuration |
|
class Config { |
|
|
|
// this bean will be injected into the OrderServiceTest class |
|
@Bean |
|
fun orderService(): OrderService { |
|
// set properties, etc. |
|
return OrderServiceImpl() |
|
} |
|
} |
|
|
|
@Test |
|
fun testOrderService() { |
|
// test the orderService |
|
} |
|
} |
|
---- |
|
<1> Loading configuration information from the nested `Config` class. |
|
|
|
|
|
[[testcontext-ctx-management-mixed-config]] |
|
=== Mixing XML, Groovy Scripts, and Component Classes |
|
|
|
It may sometimes be desirable to mix XML configuration files, Groovy scripts, and |
|
component classes (typically `@Configuration` classes) to configure an |
|
`ApplicationContext` for your tests. For example, if you use XML configuration in |
|
production, you may decide that you want to use `@Configuration` classes to configure |
|
specific Spring-managed components for your tests, or vice versa. |
|
|
|
Furthermore, some third-party frameworks (such as Spring Boot) provide first-class |
|
support for loading an `ApplicationContext` from different types of resources |
|
simultaneously (for example, XML configuration files, Groovy scripts, and |
|
`@Configuration` classes). The Spring Framework, historically, has not supported this for |
|
standard deployments. Consequently, most of the `SmartContextLoader` implementations that |
|
the Spring Framework delivers in the `spring-test` module support only one resource type |
|
for each test context. However, this does not mean that you cannot use both. One |
|
exception to the general rule is that the `GenericGroovyXmlContextLoader` and |
|
`GenericGroovyXmlWebContextLoader` support both XML configuration files and Groovy |
|
scripts simultaneously. Furthermore, third-party frameworks may choose to support the |
|
declaration of both `locations` and `classes` through `@ContextConfiguration`, and, with |
|
the standard testing support in the TestContext framework, you have the following options. |
|
|
|
If you want to use resource locations (for example, XML or Groovy) and `@Configuration` |
|
classes to configure your tests, you must pick one as the entry point, and that one must |
|
include or import the other. For example, in XML or Groovy scripts, you can include |
|
`@Configuration` classes by using component scanning or defining them as normal Spring |
|
beans, whereas, in a `@Configuration` class, you can use `@ImportResource` to import XML |
|
configuration files or Groovy scripts. Note that this behavior is semantically equivalent |
|
to how you configure your application in production: In production configuration, you |
|
define either a set of XML or Groovy resource locations or a set of `@Configuration` |
|
classes from which your production `ApplicationContext` is loaded, but you still have the |
|
freedom to include or import the other type of configuration. |
|
|
|
[[testcontext-ctx-management-initializers]] |
|
=== Context Configuration with Context Initializers |
|
|
|
To configure an `ApplicationContext` for your tests by using context initializers, |
|
annotate your test class with `@ContextConfiguration` and configure the `initializers` |
|
attribute with an array that contains references to classes that implement |
|
`ApplicationContextInitializer`. The declared context initializers are then used to |
|
initialize the `ConfigurableApplicationContext` that is loaded for your tests. Note that |
|
the concrete `ConfigurableApplicationContext` type supported by each declared initializer |
|
must be compatible with the type of `ApplicationContext` created by the |
|
`SmartContextLoader` in use (typically a `GenericApplicationContext`). Furthermore, the |
|
order in which the initializers are invoked depends on whether they implement Spring's |
|
`Ordered` interface or are annotated with Spring's `@Order` annotation or the standard |
|
`@Priority` annotation. The following example shows how to use initializers: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from TestConfig |
|
// and initialized by TestAppCtxInitializer |
|
@ContextConfiguration( |
|
classes = TestConfig.class, |
|
initializers = TestAppCtxInitializer.class) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying configuration by using a configuration class and an initializer. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from TestConfig |
|
// and initialized by TestAppCtxInitializer |
|
@ContextConfiguration( |
|
classes = [TestConfig::class], |
|
initializers = [TestAppCtxInitializer::class]) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying configuration by using a configuration class and an initializer. |
|
|
|
|
|
You can also omit the declaration of XML configuration files, Groovy scripts, or |
|
component classes in `@ContextConfiguration` entirely and instead declare only |
|
`ApplicationContextInitializer` classes, which are then responsible for registering beans |
|
in the context -- for example, by programmatically loading bean definitions from XML |
|
files or configuration classes. The following example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be initialized by EntireAppInitializer |
|
// which presumably registers beans in the context |
|
@ContextConfiguration(initializers = EntireAppInitializer.class) <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying configuration by using only an initializer. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be initialized by EntireAppInitializer |
|
// which presumably registers beans in the context |
|
@ContextConfiguration(initializers = [EntireAppInitializer::class]) // <1> |
|
class MyTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying configuration by using only an initializer. |
|
|
|
|
|
[[testcontext-ctx-management-inheritance]] |
|
=== Context Configuration Inheritance |
|
|
|
`@ContextConfiguration` supports boolean `inheritLocations` and `inheritInitializers` |
|
attributes that denote whether resource locations or component classes and context |
|
initializers declared by superclasses should be inherited. The default value for both |
|
flags is `true`. This means that a test class inherits the resource locations or |
|
component classes as well as the context initializers declared by any superclasses. |
|
Specifically, the resource locations or component classes for a test class are appended |
|
to the list of resource locations or annotated classes declared by superclasses. |
|
Similarly, the initializers for a given test class are added to the set of initializers |
|
defined by test superclasses. Thus, subclasses have the option of extending the resource |
|
locations, component classes, or context initializers. |
|
|
|
If the `inheritLocations` or `inheritInitializers` attribute in `@ContextConfiguration` |
|
is set to `false`, the resource locations or component classes and the context |
|
initializers, respectively, for the test class shadow and effectively replace the |
|
configuration defined by superclasses. |
|
|
|
NOTE: As of Spring Framework 5.3, test configuration may also be inherited from enclosing |
|
classes. See <<testcontext-junit-jupiter-nested-test-configuration>> for details. |
|
|
|
In the next example, which uses XML resource locations, the `ApplicationContext` for |
|
`ExtendedTest` is loaded from `base-config.xml` and `extended-config.xml`, in that order. |
|
Beans defined in `extended-config.xml` can, therefore, override (that is, replace) those |
|
defined in `base-config.xml`. The following example shows how one class can extend |
|
another and use both its own configuration file and the superclass's configuration file: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from "/base-config.xml" |
|
// in the root of the classpath |
|
@ContextConfiguration("/base-config.xml") <1> |
|
class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be loaded from "/base-config.xml" and |
|
// "/extended-config.xml" in the root of the classpath |
|
@ContextConfiguration("/extended-config.xml") <2> |
|
class ExtendedTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Configuration file defined in the superclass. |
|
<2> Configuration file defined in the subclass. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from "/base-config.xml" |
|
// in the root of the classpath |
|
@ContextConfiguration("/base-config.xml") // <1> |
|
open class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be loaded from "/base-config.xml" and |
|
// "/extended-config.xml" in the root of the classpath |
|
@ContextConfiguration("/extended-config.xml") // <2> |
|
class ExtendedTest : BaseTest() { |
|
// class body... |
|
} |
|
---- |
|
<1> Configuration file defined in the superclass. |
|
<2> Configuration file defined in the subclass. |
|
|
|
|
|
Similarly, in the next example, which uses component classes, the `ApplicationContext` |
|
for `ExtendedTest` is loaded from the `BaseConfig` and `ExtendedConfig` classes, in that |
|
order. Beans defined in `ExtendedConfig` can, therefore, override (that is, replace) |
|
those defined in `BaseConfig`. The following example shows how one class can extend |
|
another and use both its own configuration class and the superclass's configuration class: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// ApplicationContext will be loaded from BaseConfig |
|
@SpringJUnitConfig(BaseConfig.class) // <1> |
|
class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig |
|
@SpringJUnitConfig(ExtendedConfig.class) // <2> |
|
class ExtendedTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Configuration class defined in the superclass. |
|
<2> Configuration class defined in the subclass. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// ApplicationContext will be loaded from BaseConfig |
|
@SpringJUnitConfig(BaseConfig::class) // <1> |
|
open class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig |
|
@SpringJUnitConfig(ExtendedConfig::class) // <2> |
|
class ExtendedTest : BaseTest() { |
|
// class body... |
|
} |
|
---- |
|
<1> Configuration class defined in the superclass. |
|
<2> Configuration class defined in the subclass. |
|
|
|
|
|
In the next example, which uses context initializers, the `ApplicationContext` for |
|
`ExtendedTest` is initialized by using `BaseInitializer` and `ExtendedInitializer`. Note, |
|
however, that the order in which the initializers are invoked depends on whether they |
|
implement Spring's `Ordered` interface or are annotated with Spring's `@Order` annotation |
|
or the standard `@Priority` annotation. The following example shows how one class can |
|
extend another and use both its own initializer and the superclass's initializer: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// ApplicationContext will be initialized by BaseInitializer |
|
@SpringJUnitConfig(initializers = BaseInitializer.class) // <1> |
|
class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be initialized by BaseInitializer |
|
// and ExtendedInitializer |
|
@SpringJUnitConfig(initializers = ExtendedInitializer.class) // <2> |
|
class ExtendedTest extends BaseTest { |
|
// class body... |
|
} |
|
---- |
|
<1> Initializer defined in the superclass. |
|
<2> Initializer defined in the subclass. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// ApplicationContext will be initialized by BaseInitializer |
|
@SpringJUnitConfig(initializers = [BaseInitializer::class]) // <1> |
|
open class BaseTest { |
|
// class body... |
|
} |
|
|
|
// ApplicationContext will be initialized by BaseInitializer |
|
// and ExtendedInitializer |
|
@SpringJUnitConfig(initializers = [ExtendedInitializer::class]) // <2> |
|
class ExtendedTest : BaseTest() { |
|
// class body... |
|
} |
|
---- |
|
<1> Initializer defined in the superclass. |
|
<2> Initializer defined in the subclass. |
|
|
|
|
|
[[testcontext-ctx-management-env-profiles]] |
|
=== Context Configuration with Environment Profiles |
|
|
|
The Spring Framework has first-class support for the notion of environments and profiles |
|
(AKA "bean definition profiles"), and integration tests can be configured to activate |
|
particular bean definition profiles for various testing scenarios. This is achieved by |
|
annotating a test class with the `@ActiveProfiles` annotation and supplying a list of |
|
profiles that should be activated when loading the `ApplicationContext` for the test. |
|
|
|
NOTE: You can use `@ActiveProfiles` with any implementation of the `SmartContextLoader` |
|
SPI, but `@ActiveProfiles` is not supported with implementations of the older |
|
`ContextLoader` SPI. |
|
|
|
Consider two examples with XML configuration and `@Configuration` classes: |
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"] |
|
---- |
|
<!-- app-config.xml --> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jdbc="http://www.springframework.org/schema/jdbc" |
|
xmlns:jee="http://www.springframework.org/schema/jee" |
|
xsi:schemaLocation="..."> |
|
|
|
<bean id="transferService" |
|
class="com.bank.service.internal.DefaultTransferService"> |
|
<constructor-arg ref="accountRepository"/> |
|
<constructor-arg ref="feePolicy"/> |
|
</bean> |
|
|
|
<bean id="accountRepository" |
|
class="com.bank.repository.internal.JdbcAccountRepository"> |
|
<constructor-arg ref="dataSource"/> |
|
</bean> |
|
|
|
<bean id="feePolicy" |
|
class="com.bank.service.internal.ZeroFeePolicy"/> |
|
|
|
<beans profile="dev"> |
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script |
|
location="classpath:com/bank/config/sql/schema.sql"/> |
|
<jdbc:script |
|
location="classpath:com/bank/config/sql/test-data.sql"/> |
|
</jdbc:embedded-database> |
|
</beans> |
|
|
|
<beans profile="production"> |
|
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> |
|
</beans> |
|
|
|
<beans profile="default"> |
|
<jdbc:embedded-database id="dataSource"> |
|
<jdbc:script |
|
location="classpath:com/bank/config/sql/schema.sql"/> |
|
</jdbc:embedded-database> |
|
</beans> |
|
|
|
</beans> |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// ApplicationContext will be loaded from "classpath:/app-config.xml" |
|
@ContextConfiguration("/app-config.xml") |
|
@ActiveProfiles("dev") |
|
class TransferServiceTest { |
|
|
|
@Autowired |
|
TransferService transferService; |
|
|
|
@Test |
|
void testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// ApplicationContext will be loaded from "classpath:/app-config.xml" |
|
@ContextConfiguration("/app-config.xml") |
|
@ActiveProfiles("dev") |
|
class TransferServiceTest { |
|
|
|
@Autowired |
|
lateinit var transferService: TransferService |
|
|
|
@Test |
|
fun testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
|
|
When `TransferServiceTest` is run, its `ApplicationContext` is loaded from the |
|
`app-config.xml` configuration file in the root of the classpath. If you inspect |
|
`app-config.xml`, you can see that the `accountRepository` bean has a dependency on a |
|
`dataSource` bean. However, `dataSource` is not defined as a top-level bean. Instead, |
|
`dataSource` is defined three times: in the `production` profile, in the `dev` profile, |
|
and in the `default` profile. |
|
|
|
By annotating `TransferServiceTest` with `@ActiveProfiles("dev")`, we instruct the Spring |
|
TestContext Framework to load the `ApplicationContext` with the active profiles set to |
|
`{"dev"}`. As a result, an embedded database is created and populated with test data, and |
|
the `accountRepository` bean is wired with a reference to the development `DataSource`. |
|
That is likely what we want in an integration test. |
|
|
|
It is sometimes useful to assign beans to a `default` profile. Beans within the default |
|
profile are included only when no other profile is specifically activated. You can use |
|
this to define "`fallback`" beans to be used in the application's default state. For |
|
example, you may explicitly provide a data source for `dev` and `production` profiles, |
|
but define an in-memory data source as a default when neither of these is active. |
|
|
|
The following code listings demonstrate how to implement the same configuration and |
|
integration test with `@Configuration` classes instead of XML: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@Profile("dev") |
|
public class StandaloneDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.addScript("classpath:com/bank/config/sql/test-data.sql") |
|
.build(); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@Profile("dev") |
|
class StandaloneDataConfig { |
|
|
|
@Bean |
|
fun dataSource(): DataSource { |
|
return EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.addScript("classpath:com/bank/config/sql/test-data.sql") |
|
.build() |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@Profile("production") |
|
public class JndiDataConfig { |
|
|
|
@Bean(destroyMethod="") |
|
public DataSource dataSource() throws Exception { |
|
Context ctx = new InitialContext(); |
|
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@Profile("production") |
|
class JndiDataConfig { |
|
|
|
@Bean(destroyMethod = "") |
|
fun dataSource(): DataSource { |
|
val ctx = InitialContext() |
|
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@Profile("default") |
|
public class DefaultDataConfig { |
|
|
|
@Bean |
|
public DataSource dataSource() { |
|
return new EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.build(); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@Profile("default") |
|
class DefaultDataConfig { |
|
|
|
@Bean |
|
fun dataSource(): DataSource { |
|
return EmbeddedDatabaseBuilder() |
|
.setType(EmbeddedDatabaseType.HSQL) |
|
.addScript("classpath:com/bank/config/sql/schema.sql") |
|
.build() |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
public class TransferServiceConfig { |
|
|
|
@Autowired DataSource dataSource; |
|
|
|
@Bean |
|
public TransferService transferService() { |
|
return new DefaultTransferService(accountRepository(), feePolicy()); |
|
} |
|
|
|
@Bean |
|
public AccountRepository accountRepository() { |
|
return new JdbcAccountRepository(dataSource); |
|
} |
|
|
|
@Bean |
|
public FeePolicy feePolicy() { |
|
return new ZeroFeePolicy(); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
class TransferServiceConfig { |
|
|
|
@Autowired |
|
lateinit var dataSource: DataSource |
|
|
|
@Bean |
|
fun transferService(): TransferService { |
|
return DefaultTransferService(accountRepository(), feePolicy()) |
|
} |
|
|
|
@Bean |
|
fun accountRepository(): AccountRepository { |
|
return JdbcAccountRepository(dataSource) |
|
} |
|
|
|
@Bean |
|
fun feePolicy(): FeePolicy { |
|
return ZeroFeePolicy() |
|
} |
|
} |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig({ |
|
TransferServiceConfig.class, |
|
StandaloneDataConfig.class, |
|
JndiDataConfig.class, |
|
DefaultDataConfig.class}) |
|
@ActiveProfiles("dev") |
|
class TransferServiceTest { |
|
|
|
@Autowired |
|
TransferService transferService; |
|
|
|
@Test |
|
void testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig( |
|
TransferServiceConfig::class, |
|
StandaloneDataConfig::class, |
|
JndiDataConfig::class, |
|
DefaultDataConfig::class) |
|
@ActiveProfiles("dev") |
|
class TransferServiceTest { |
|
|
|
@Autowired |
|
lateinit var transferService: TransferService |
|
|
|
@Test |
|
fun testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
|
|
In this variation, we have split the XML configuration into four independent |
|
`@Configuration` classes: |
|
|
|
* `TransferServiceConfig`: Acquires a `dataSource` through dependency injection by using |
|
`@Autowired`. |
|
* `StandaloneDataConfig`: Defines a `dataSource` for an embedded database suitable for |
|
developer tests. |
|
* `JndiDataConfig`: Defines a `dataSource` that is retrieved from JNDI in a production |
|
environment. |
|
* `DefaultDataConfig`: Defines a `dataSource` for a default embedded database, in case no |
|
profile is active. |
|
|
|
As with the XML-based configuration example, we still annotate `TransferServiceTest` with |
|
`@ActiveProfiles("dev")`, but this time we specify all four configuration classes by |
|
using the `@ContextConfiguration` annotation. The body of the test class itself remains |
|
completely unchanged. |
|
|
|
It is often the case that a single set of profiles is used across multiple test classes |
|
within a given project. Thus, to avoid duplicate declarations of the `@ActiveProfiles` |
|
annotation, you can declare `@ActiveProfiles` once on a base class, and subclasses |
|
automatically inherit the `@ActiveProfiles` configuration from the base class. In the |
|
following example, the declaration of `@ActiveProfiles` (as well as other annotations) |
|
has been moved to an abstract superclass, `AbstractIntegrationTest`: |
|
|
|
NOTE: As of Spring Framework 5.3, test configuration may also be inherited from enclosing |
|
classes. See <<testcontext-junit-jupiter-nested-test-configuration>> for details. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig({ |
|
TransferServiceConfig.class, |
|
StandaloneDataConfig.class, |
|
JndiDataConfig.class, |
|
DefaultDataConfig.class}) |
|
@ActiveProfiles("dev") |
|
abstract class AbstractIntegrationTest { |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig( |
|
TransferServiceConfig::class, |
|
StandaloneDataConfig::class, |
|
JndiDataConfig::class, |
|
DefaultDataConfig::class) |
|
@ActiveProfiles("dev") |
|
abstract class AbstractIntegrationTest { |
|
} |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// "dev" profile inherited from superclass |
|
class TransferServiceTest extends AbstractIntegrationTest { |
|
|
|
@Autowired |
|
TransferService transferService; |
|
|
|
@Test |
|
void testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// "dev" profile inherited from superclass |
|
class TransferServiceTest : AbstractIntegrationTest() { |
|
|
|
@Autowired |
|
lateinit var transferService: TransferService |
|
|
|
@Test |
|
fun testTransferService() { |
|
// test the transferService |
|
} |
|
} |
|
---- |
|
|
|
`@ActiveProfiles` also supports an `inheritProfiles` attribute that can be used to |
|
disable the inheritance of active profiles, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// "dev" profile overridden with "production" |
|
@ActiveProfiles(profiles = "production", inheritProfiles = false) |
|
class ProductionTransferServiceTest extends AbstractIntegrationTest { |
|
// test body |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// "dev" profile overridden with "production" |
|
@ActiveProfiles("production", inheritProfiles = false) |
|
class ProductionTransferServiceTest : AbstractIntegrationTest() { |
|
// test body |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-env-profiles-ActiveProfilesResolver]] |
|
Furthermore, it is sometimes necessary to resolve active profiles for tests |
|
programmatically instead of declaratively -- for example, based on: |
|
|
|
* The current operating system. |
|
* Whether tests are being run on a continuous integration build server. |
|
* The presence of certain environment variables. |
|
* The presence of custom class-level annotations. |
|
* Other concerns. |
|
|
|
To resolve active bean definition profiles programmatically, you can implement |
|
a custom `ActiveProfilesResolver` and register it by using the `resolver` |
|
attribute of `@ActiveProfiles`. For further information, see the corresponding |
|
{api-spring-framework}/test/context/ActiveProfilesResolver.html[javadoc]. |
|
The following example demonstrates how to implement and register a custom |
|
`OperatingSystemActiveProfilesResolver`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// "dev" profile overridden programmatically via a custom resolver |
|
@ActiveProfiles( |
|
resolver = OperatingSystemActiveProfilesResolver.class, |
|
inheritProfiles = false) |
|
class TransferServiceTest extends AbstractIntegrationTest { |
|
// test body |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// "dev" profile overridden programmatically via a custom resolver |
|
@ActiveProfiles( |
|
resolver = OperatingSystemActiveProfilesResolver::class, |
|
inheritProfiles = false) |
|
class TransferServiceTest : AbstractIntegrationTest() { |
|
// test body |
|
} |
|
---- |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver { |
|
|
|
@Override |
|
public String[] resolve(Class<?> testClass) { |
|
String profile = ...; |
|
// determine the value of profile based on the operating system |
|
return new String[] {profile}; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class OperatingSystemActiveProfilesResolver : ActiveProfilesResolver { |
|
|
|
override fun resolve(testClass: Class<*>): Array<String> { |
|
val profile: String = ... |
|
// determine the value of profile based on the operating system |
|
return arrayOf(profile) |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-property-sources]] |
|
=== Context Configuration with Test Property Sources |
|
|
|
The Spring Framework has first-class support for the notion of an environment with a |
|
hierarchy of property sources, and you can configure integration tests with test-specific |
|
property sources. In contrast to the `@PropertySource` annotation used on |
|
`@Configuration` classes, you can declare the `@TestPropertySource` annotation on a test |
|
class to declare resource locations for test properties files or inlined properties. |
|
These test property sources are added to the set of `PropertySources` in the |
|
`Environment` for the `ApplicationContext` loaded for the annotated integration test. |
|
|
|
[NOTE] |
|
==== |
|
You can use `@TestPropertySource` with any implementation of the `SmartContextLoader` |
|
SPI, but `@TestPropertySource` is not supported with implementations of the older |
|
`ContextLoader` SPI. |
|
|
|
Implementations of `SmartContextLoader` gain access to merged test property source values |
|
through the `getPropertySourceLocations()` and `getPropertySourceProperties()` methods in |
|
`MergedContextConfiguration`. |
|
==== |
|
|
|
==== Declaring Test Property Sources |
|
|
|
You can configure test properties files by using the `locations` or `value` attribute of |
|
`@TestPropertySource`. |
|
|
|
Both traditional and XML-based properties file formats are supported -- for example, |
|
`"classpath:/com/example/test.properties"` or `"file:///path/to/file.xml"`. |
|
|
|
Each path is interpreted as a Spring `Resource`. A plain path (for example, |
|
`"test.properties"`) is treated as a classpath resource that is relative to the package |
|
in which the test class is defined. A path starting with a slash is treated as an |
|
absolute classpath resource (for example: `"/org/example/test.xml"`). A path that |
|
references a URL (for example, a path prefixed with `classpath:`, `file:`, or `http:`) is |
|
loaded by using the specified resource protocol. Resource location wildcards (such as |
|
`**/*.properties`) are not permitted: Each location must evaluate to exactly one |
|
`.properties` or `.xml` resource. |
|
|
|
The following example uses a test properties file: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource("/test.properties") // <1> |
|
class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying a properties file with an absolute path. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource("/test.properties") // <1> |
|
class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
<1> Specifying a properties file with an absolute path. |
|
|
|
|
|
You can configure inlined properties in the form of key-value pairs by using the |
|
`properties` attribute of `@TestPropertySource`, as shown in the next example. All |
|
key-value pairs are added to the enclosing `Environment` as a single test |
|
`PropertySource` with the highest precedence. |
|
|
|
The supported syntax for key-value pairs is the same as the syntax defined for entries in |
|
a Java properties file: |
|
|
|
* `key=value` |
|
* `key:value` |
|
* `key value` |
|
|
|
The following example sets two 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> Setting two properties by using two variations of the key-value syntax. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) // <1> |
|
class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
<1> Setting two properties by using two variations of the key-value syntax. |
|
|
|
[NOTE] |
|
==== |
|
As of Spring Framework 5.2, `@TestPropertySource` can be used as _repeatable annotation_. |
|
That means that you can have multiple declarations of `@TestPropertySource` on a single |
|
test class, with the `locations` and `properties` from later `@TestPropertySource` |
|
annotations overriding those from previous `@TestPropertySource` annotations. |
|
|
|
In addition, you may declare multiple composed annotations on a test class that are each |
|
meta-annotated with `@TestPropertySource`, and all of those `@TestPropertySource` |
|
declarations will contribute to your test property sources. |
|
|
|
Directly present `@TestPropertySource` annotations always take precedence over |
|
meta-present `@TestPropertySource` annotations. In other words, `locations` and |
|
`properties` from a directly present `@TestPropertySource` annotation will override the |
|
`locations` and `properties` from a `@TestPropertySource` annotation used as a |
|
meta-annotation. |
|
==== |
|
|
|
|
|
==== Default Properties File Detection |
|
|
|
If `@TestPropertySource` is declared as an empty annotation (that is, without explicit |
|
values for the `locations` or `properties` attributes), an attempt is made to detect a |
|
default properties file relative to the class that declared the annotation. For example, |
|
if the annotated test class is `com.example.MyTest`, the corresponding default properties |
|
file is `classpath:com/example/MyTest.properties`. If the default cannot be detected, an |
|
`IllegalStateException` is thrown. |
|
|
|
==== Precedence |
|
|
|
Test properties have higher precedence than those defined in the operating system's |
|
environment, Java system properties, or property sources added by the application |
|
declaratively by using `@PropertySource` or programmatically. Thus, test properties can |
|
be used to selectively override properties loaded from system and application property |
|
sources. Furthermore, inlined properties have higher precedence than properties loaded |
|
from resource locations. Note, however, that properties registered via |
|
<<testcontext-ctx-management-dynamic-property-sources, `@DynamicPropertySource`>> have |
|
higher precedence than those loaded via `@TestPropertySource`. |
|
|
|
In the next example, the `timezone` and `port` properties and any properties defined in |
|
`"/test.properties"` override any properties of the same name that are defined in system |
|
and application property sources. Furthermore, if the `"/test.properties"` file defines |
|
entries for the `timezone` and `port` properties those are overridden by the inlined |
|
properties declared by using the `properties` attribute. The following example shows how |
|
to specify properties both in a file and inline: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource( |
|
locations = "/test.properties", |
|
properties = {"timezone = GMT", "port: 4242"} |
|
) |
|
class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ContextConfiguration |
|
@TestPropertySource("/test.properties", |
|
properties = ["timezone = GMT", "port: 4242"] |
|
) |
|
class MyIntegrationTests { |
|
// class body... |
|
} |
|
---- |
|
|
|
==== Inheriting and Overriding Test Property Sources |
|
|
|
`@TestPropertySource` supports boolean `inheritLocations` and `inheritProperties` |
|
attributes that denote whether resource locations for properties files and inlined |
|
properties declared by superclasses should be inherited. The default value for both flags |
|
is `true`. This means that a test class inherits the locations and inlined properties |
|
declared by any superclasses. Specifically, the locations and inlined properties for a |
|
test class are appended to the locations and inlined properties declared by superclasses. |
|
Thus, subclasses have the option of extending the locations and inlined properties. Note |
|
that properties that appear later shadow (that is, override) properties of the same name |
|
that appear earlier. In addition, the aforementioned precedence rules apply for inherited |
|
test property sources as well. |
|
|
|
If the `inheritLocations` or `inheritProperties` attribute in `@TestPropertySource` is |
|
set to `false`, the locations or inlined properties, respectively, for the test class |
|
shadow and effectively replace the configuration defined by superclasses. |
|
|
|
NOTE: As of Spring Framework 5.3, test configuration may also be inherited from enclosing |
|
classes. See <<testcontext-junit-jupiter-nested-test-configuration>> for details. |
|
|
|
In the next example, the `ApplicationContext` for `BaseTest` is loaded by using only the |
|
`base.properties` file as a test property source. In contrast, the `ApplicationContext` |
|
for `ExtendedTest` is loaded by using the `base.properties` and `extended.properties` |
|
files as test property source locations. The following example shows how to define |
|
properties in both a subclass and its superclass by using `properties` files: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@TestPropertySource("base.properties") |
|
@ContextConfiguration |
|
class BaseTest { |
|
// ... |
|
} |
|
|
|
@TestPropertySource("extended.properties") |
|
@ContextConfiguration |
|
class ExtendedTest extends BaseTest { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@TestPropertySource("base.properties") |
|
@ContextConfiguration |
|
open class BaseTest { |
|
// ... |
|
} |
|
|
|
@TestPropertySource("extended.properties") |
|
@ContextConfiguration |
|
class ExtendedTest : BaseTest() { |
|
// ... |
|
} |
|
---- |
|
|
|
In the next example, the `ApplicationContext` for `BaseTest` is loaded by using only the |
|
inlined `key1` property. In contrast, the `ApplicationContext` for `ExtendedTest` is |
|
loaded by using the inlined `key1` and `key2` properties. The following example shows how |
|
to define properties in both a subclass and its superclass by using inline properties: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@TestPropertySource(properties = "key1 = value1") |
|
@ContextConfiguration |
|
class BaseTest { |
|
// ... |
|
} |
|
|
|
@TestPropertySource(properties = "key2 = value2") |
|
@ContextConfiguration |
|
class ExtendedTest extends BaseTest { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@TestPropertySource(properties = ["key1 = value1"]) |
|
@ContextConfiguration |
|
open class BaseTest { |
|
// ... |
|
} |
|
|
|
@TestPropertySource(properties = ["key2 = value2"]) |
|
@ContextConfiguration |
|
class ExtendedTest : BaseTest() { |
|
// ... |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-dynamic-property-sources]] |
|
=== Context Configuration with Dynamic Property Sources |
|
|
|
As of Spring Framework 5.2.5, the TestContext framework provides support for _dynamic_ |
|
properties via the `@DynamicPropertySource` annotation. This annotation can be used in |
|
integration tests that need to add properties with dynamic values to the set of |
|
`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the |
|
integration test. |
|
|
|
[NOTE] |
|
==== |
|
The `@DynamicPropertySource` annotation and its supporting infrastructure were |
|
originally designed to allow properties from |
|
https://www.testcontainers.org/[Testcontainers] based tests to be exposed easily to |
|
Spring integration tests. However, this feature may also be used with any form of |
|
external resource whose lifecycle is maintained outside the test's `ApplicationContext`. |
|
==== |
|
|
|
In contrast to the <<testcontext-ctx-management-property-sources,`@TestPropertySource`>> |
|
annotation that is applied at the class level, `@DynamicPropertySource` must be applied |
|
to a `static` method that accepts a single `DynamicPropertyRegistry` argument which is |
|
used to add _name-value_ pairs to the `Environment`. Values are dynamic and provided via |
|
a `Supplier` which is only invoked when the property is resolved. Typically, method |
|
references are used to supply values, as can be seen in the following example which uses |
|
the Testcontainers project to manage a Redis container outside of the Spring |
|
`ApplicationContext`. The IP address and port of the managed Redis container are made |
|
available to components within the test's `ApplicationContext` via the `redis.host` and |
|
`redis.port` properties. These properties can be accessed via Spring's `Environment` |
|
abstraction or injected directly into Spring-managed components – for example, via |
|
`@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively. |
|
|
|
[TIP] |
|
==== |
|
If you use `@DynamicPropertySource` in a base class and discover that tests in subclasses |
|
fail because the dynamic properties change between subclasses, you may need to annotate |
|
your base class with <<spring-testing-annotation-dirtiescontext, `@DirtiesContext`>> to |
|
ensure that each subclass gets its own `ApplicationContext` with the correct dynamic |
|
properties. |
|
==== |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(/* ... */) |
|
@Testcontainers |
|
class ExampleIntegrationTests { |
|
|
|
@Container |
|
static GenericContainer redis = |
|
new GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379); |
|
|
|
@DynamicPropertySource |
|
static void redisProperties(DynamicPropertyRegistry registry) { |
|
registry.add("redis.host", redis::getHost); |
|
registry.add("redis.port", redis::getFirstMappedPort); |
|
} |
|
|
|
// tests ... |
|
|
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(/* ... */) |
|
@Testcontainers |
|
class ExampleIntegrationTests { |
|
|
|
companion object { |
|
|
|
@Container |
|
@JvmStatic |
|
val redis: GenericContainer = |
|
GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379) |
|
|
|
@DynamicPropertySource |
|
@JvmStatic |
|
fun redisProperties(registry: DynamicPropertyRegistry) { |
|
registry.add("redis.host", redis::getHost) |
|
registry.add("redis.port", redis::getFirstMappedPort) |
|
} |
|
} |
|
|
|
// tests ... |
|
|
|
} |
|
---- |
|
|
|
==== Precedence |
|
|
|
Dynamic properties have higher precedence than those loaded from `@TestPropertySource`, |
|
the operating system's environment, Java system properties, or property sources added by |
|
the application declaratively by using `@PropertySource` or programmatically. Thus, |
|
dynamic properties can be used to selectively override properties loaded via |
|
`@TestPropertySource`, system property sources, and application property sources. |
|
|
|
[[testcontext-ctx-management-web]] |
|
=== Loading a `WebApplicationContext` |
|
|
|
To instruct the TestContext framework to load a `WebApplicationContext` instead of a |
|
standard `ApplicationContext`, you can annotate the respective test class with |
|
`@WebAppConfiguration`. |
|
|
|
The presence of `@WebAppConfiguration` on your test class instructs the TestContext |
|
framework (TCF) that a `WebApplicationContext` (WAC) should be loaded for your |
|
integration tests. In the background, the TCF makes sure that a `MockServletContext` is |
|
created and supplied to your test's WAC. By default, the base resource path for your |
|
`MockServletContext` is set to `src/main/webapp`. This is interpreted as a path relative |
|
to the root of your JVM (normally the path to your project). If you are familiar with the |
|
directory structure of a web application in a Maven project, you know that |
|
`src/main/webapp` is the default location for the root of your WAR. If you need to |
|
override this default, you can provide an alternate path to the `@WebAppConfiguration` |
|
annotation (for example, `@WebAppConfiguration("src/test/webapp")`). If you wish to |
|
reference a base resource path from the classpath instead of the file system, you can use |
|
Spring's `classpath:` prefix. |
|
|
|
Note that Spring's testing support for `WebApplicationContext` implementations is on par |
|
with its support for standard `ApplicationContext` implementations. When testing with a |
|
`WebApplicationContext`, you are free to declare XML configuration files, Groovy scripts, |
|
or `@Configuration` classes by using `@ContextConfiguration`. You are also free to use |
|
any other test annotations, such as `@ActiveProfiles`, `@TestExecutionListeners`, `@Sql`, |
|
`@Rollback`, and others. |
|
|
|
The remaining examples in this section show some of the various configuration options for |
|
loading a `WebApplicationContext`. The following example shows the TestContext |
|
framework's support for convention over configuration: |
|
|
|
.Conventions |
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
|
|
// defaults to "file:src/main/webapp" |
|
@WebAppConfiguration |
|
|
|
// detects "WacTests-context.xml" in the same package |
|
// or static nested @Configuration classes |
|
@ContextConfiguration |
|
class WacTests { |
|
//... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
|
|
// defaults to "file:src/main/webapp" |
|
@WebAppConfiguration |
|
|
|
// detects "WacTests-context.xml" in the same package |
|
// or static nested @Configuration classes |
|
@ContextConfiguration |
|
class WacTests { |
|
//... |
|
} |
|
---- |
|
|
|
If you annotate a test class with `@WebAppConfiguration` without specifying a resource |
|
base path, the resource path effectively defaults to `file:src/main/webapp`. Similarly, |
|
if you declare `@ContextConfiguration` without specifying resource `locations`, component |
|
`classes`, or context `initializers`, Spring tries to detect the presence of your |
|
configuration by using conventions (that is, `WacTests-context.xml` in the same package |
|
as the `WacTests` class or static nested `@Configuration` classes). |
|
|
|
The following example shows how to explicitly declare a resource base path with |
|
`@WebAppConfiguration` and an XML resource location with `@ContextConfiguration`: |
|
|
|
.Default resource semantics |
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
|
|
// file system resource |
|
@WebAppConfiguration("webapp") |
|
|
|
// classpath resource |
|
@ContextConfiguration("/spring/test-servlet-config.xml") |
|
class WacTests { |
|
//... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
|
|
// file system resource |
|
@WebAppConfiguration("webapp") |
|
|
|
// classpath resource |
|
@ContextConfiguration("/spring/test-servlet-config.xml") |
|
class WacTests { |
|
//... |
|
} |
|
---- |
|
|
|
The important thing to note here is the different semantics for paths with these two |
|
annotations. By default, `@WebAppConfiguration` resource paths are file system based, |
|
whereas `@ContextConfiguration` resource locations are classpath based. |
|
|
|
The following example shows that we can override the default resource semantics for both |
|
annotations by specifying a Spring resource prefix: |
|
|
|
.Explicit resource semantics |
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
|
|
// classpath resource |
|
@WebAppConfiguration("classpath:test-web-resources") |
|
|
|
// file system resource |
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml") |
|
class WacTests { |
|
//... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
|
|
// classpath resource |
|
@WebAppConfiguration("classpath:test-web-resources") |
|
|
|
// file system resource |
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml") |
|
class WacTests { |
|
//... |
|
} |
|
---- |
|
|
|
Contrast the comments in this example with the previous example. |
|
|
|
[[testcontext-ctx-management-web-mocks]] |
|
=== Working with Web Mocks |
|
|
|
To provide comprehensive web testing support, the TestContext framework has a |
|
`ServletTestExecutionListener` that is enabled by default. When testing against a |
|
`WebApplicationContext`, this <<testcontext-key-abstractions, `TestExecutionListener`>> |
|
sets up default thread-local state by using Spring Web's `RequestContextHolder` before |
|
each test method and creates a `MockHttpServletRequest`, a `MockHttpServletResponse`, and |
|
a `ServletWebRequest` based on the base resource path configured with |
|
`@WebAppConfiguration`. `ServletTestExecutionListener` also ensures that the |
|
`MockHttpServletResponse` and `ServletWebRequest` can be injected into the test instance, |
|
and, once the test is complete, it cleans up thread-local state. |
|
|
|
Once you have a `WebApplicationContext` loaded for your test, you might find that you |
|
need to interact with the web mocks -- for example, to set up your test fixture or to |
|
perform assertions after invoking your web component. The following example shows which |
|
mocks can be autowired into your test instance. Note that the `WebApplicationContext` and |
|
`MockServletContext` are both cached across the test suite, whereas the other mocks are |
|
managed per test method by the `ServletTestExecutionListener`. |
|
|
|
.Injecting mocks |
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitWebConfig |
|
class WacTests { |
|
|
|
@Autowired |
|
WebApplicationContext wac; // cached |
|
|
|
@Autowired |
|
MockServletContext servletContext; // cached |
|
|
|
@Autowired |
|
MockHttpSession session; |
|
|
|
@Autowired |
|
MockHttpServletRequest request; |
|
|
|
@Autowired |
|
MockHttpServletResponse response; |
|
|
|
@Autowired |
|
ServletWebRequest webRequest; |
|
|
|
//... |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitWebConfig |
|
class WacTests { |
|
|
|
@Autowired |
|
lateinit var wac: WebApplicationContext // cached |
|
|
|
@Autowired |
|
lateinit var servletContext: MockServletContext // cached |
|
|
|
@Autowired |
|
lateinit var session: MockHttpSession |
|
|
|
@Autowired |
|
lateinit var request: MockHttpServletRequest |
|
|
|
@Autowired |
|
lateinit var response: MockHttpServletResponse |
|
|
|
@Autowired |
|
lateinit var webRequest: ServletWebRequest |
|
|
|
//... |
|
} |
|
---- |
|
|
|
[[testcontext-ctx-management-caching]] |
|
=== Context Caching |
|
|
|
Once the TestContext framework loads an `ApplicationContext` (or `WebApplicationContext`) |
|
for a test, that context is cached and reused for all subsequent tests that declare the |
|
same unique context configuration within the same test suite. To understand how caching |
|
works, it is important to understand what is meant by "`unique`" and "`test suite.`" |
|
|
|
An `ApplicationContext` can be uniquely identified by the combination of configuration |
|
parameters that is used to load it. Consequently, the unique combination of configuration |
|
parameters is used to generate a key under which the context is cached. The TestContext |
|
framework uses the following configuration parameters to build the context cache key: |
|
|
|
* `locations` (from `@ContextConfiguration`) |
|
* `classes` (from `@ContextConfiguration`) |
|
* `contextInitializerClasses` (from `@ContextConfiguration`) |
|
* `contextCustomizers` (from `ContextCustomizerFactory`) – this includes |
|
`@DynamicPropertySource` methods as well as various features from Spring Boot's |
|
testing support such as `@MockBean` and `@SpyBean`. |
|
* `contextLoader` (from `@ContextConfiguration`) |
|
* `parent` (from `@ContextHierarchy`) |
|
* `activeProfiles` (from `@ActiveProfiles`) |
|
* `propertySourceLocations` (from `@TestPropertySource`) |
|
* `propertySourceProperties` (from `@TestPropertySource`) |
|
* `resourceBasePath` (from `@WebAppConfiguration`) |
|
|
|
For example, if `TestClassA` specifies `{"app-config.xml", "test-config.xml"}` for the |
|
`locations` (or `value`) attribute of `@ContextConfiguration`, the TestContext framework |
|
loads the corresponding `ApplicationContext` and stores it in a `static` context cache |
|
under a key that is based solely on those locations. So, if `TestClassB` also defines |
|
`{"app-config.xml", "test-config.xml"}` for its locations (either explicitly or |
|
implicitly through inheritance) but does not define `@WebAppConfiguration`, a different |
|
`ContextLoader`, different active profiles, different context initializers, different |
|
test property sources, or a different parent context, then the same `ApplicationContext` |
|
is shared by both test classes. This means that the setup cost for loading an application |
|
context is incurred only once (per test suite), and subsequent test execution is much |
|
faster. |
|
|
|
.Test suites and forked processes |
|
[NOTE] |
|
==== |
|
The Spring TestContext framework stores application contexts in a static cache. This |
|
means that the context is literally stored in a `static` variable. In other words, if |
|
tests run in separate processes, the static cache is cleared between each test |
|
execution, which effectively disables the caching mechanism. |
|
|
|
To benefit from the caching mechanism, all tests must run within the same process or test |
|
suite. This can be achieved by executing all tests as a group within an IDE. Similarly, |
|
when executing tests with a build framework such as Ant, Maven, or Gradle, it is |
|
important to make sure that the build framework does not fork between tests. For example, |
|
if the |
|
https://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html#forkMode[`forkMode`] |
|
for the Maven Surefire plug-in is set to `always` or `pertest`, the TestContext framework |
|
cannot cache application contexts between test classes, and the build process runs |
|
significantly more slowly as a result. |
|
==== |
|
|
|
The size of the context cache is bounded with a default maximum size of 32. Whenever the |
|
maximum size is reached, a least recently used (LRU) eviction policy is used to evict and |
|
close stale contexts. You can configure the maximum size from the command line or a build |
|
script by setting a JVM system property named `spring.test.context.cache.maxSize`. As an |
|
alternative, you can set the same property via the |
|
<<appendix.adoc#appendix-spring-properties,`SpringProperties`>> mechanism. |
|
|
|
Since having a large number of application contexts loaded within a given test suite can |
|
cause the suite to take an unnecessarily long time to run, it is often beneficial to |
|
know exactly how many contexts have been loaded and cached. To view the statistics for |
|
the underlying context cache, you can set the log level for the |
|
`org.springframework.test.context.cache` logging category to `DEBUG`. |
|
|
|
In the unlikely case that a test corrupts the application context and requires reloading |
|
(for example, by modifying a bean definition or the state of an application object), you |
|
can annotate your test class or test method with `@DirtiesContext` (see the discussion of |
|
`@DirtiesContext` in <<spring-testing-annotation-dirtiescontext, Spring Testing |
|
Annotations>>). This instructs Spring to remove the context from the cache and rebuild |
|
the application context before running the next test that requires the same application |
|
context. Note that support for the `@DirtiesContext` annotation is provided by the |
|
`DirtiesContextBeforeModesTestExecutionListener` and the |
|
`DirtiesContextTestExecutionListener`, which are enabled by default. |
|
|
|
.ApplicationContext lifecycle and console logging |
|
[NOTE] |
|
==== |
|
When you need to debug a test executed with the Spring TestContext Framework, it can be |
|
useful to analyze the console output (that is, output to the `SYSOUT` and `SYSERR` |
|
streams). Some build tools and IDEs are able to associate console output with a given |
|
test; however, some console output cannot be easily associated with a given test. |
|
|
|
With regard to console logging triggered by the Spring Framework itself or by components |
|
registered in the `ApplicationContext`, it is important to understand the lifecycle of an |
|
`ApplicationContext` that has been loaded by the Spring TestContext Framework within a |
|
test suite. |
|
|
|
The `ApplicationContext` for a test is typically loaded when an instance of the test |
|
class is being prepared -- for example, to perform dependency injection into `@Autowired` |
|
fields of the test instance. This means that any console logging triggered during the |
|
initialization of the `ApplicationContext` typically cannot be associated with an |
|
individual test method. However, if the context is closed immediately before the |
|
execution of a test method according to <<spring-testing-annotation-dirtiescontext>> |
|
semantics, a new instance of the context will be loaded just prior to execution of the |
|
test method. In the latter scenario, an IDE or build tool may potentially associate |
|
console logging with the individual test method. |
|
|
|
The `ApplicationContext` for a test can be closed via one of the following scenarios. |
|
|
|
* The context is closed according to `@DirtiesContext` semantics. |
|
* The context is closed because it has been automatically evicted from the cache |
|
according to the LRU eviction policy. |
|
* The context is closed via a JVM shutdown hook when the JVM for the test suite |
|
terminates. |
|
|
|
If the context is closed according to `@DirtiesContext` semantics after a particular test |
|
method, an IDE or build tool may potentially associate console logging with the |
|
individual test method. If the context is closed according to `@DirtiesContext` semantics |
|
after a test class, any console logging triggered during the shutdown of the |
|
`ApplicationContext` cannot be associated with an individual test method. Similarly, any |
|
console logging triggered during the shutdown phase via a JVM shutdown hook cannot be |
|
associated with an individual test method. |
|
|
|
When a Spring `ApplicationContext` is closed via a JVM shutdown hook, callbacks executed |
|
during the shutdown phase are executed on a thread named `SpringContextShutdownHook`. So, |
|
if you wish to disable console logging triggered when the `ApplicationContext` is closed |
|
via a JVM shutdown hook, you may be able to register a custom filter with your logging |
|
framework that allows you to ignore any logging initiated by that thread. |
|
==== |
|
|
|
[[testcontext-ctx-management-ctx-hierarchies]] |
|
=== Context Hierarchies |
|
|
|
When writing integration tests that rely on a loaded Spring `ApplicationContext`, it is |
|
often sufficient to test against a single context. However, there are times when it is |
|
beneficial or even necessary to test against a hierarchy of `ApplicationContext` |
|
instances. For example, if you are developing a Spring MVC web application, you typically |
|
have a root `WebApplicationContext` loaded by Spring's `ContextLoaderListener` and a |
|
child `WebApplicationContext` loaded by Spring's `DispatcherServlet`. This results in a |
|
parent-child context hierarchy where shared components and infrastructure configuration |
|
are declared in the root context and consumed in the child context by web-specific |
|
components. Another use case can be found in Spring Batch applications, where you often |
|
have a parent context that provides configuration for shared batch infrastructure and a |
|
child context for the configuration of a specific batch job. |
|
|
|
You can write integration tests that use context hierarchies by declaring context |
|
configuration with the `@ContextHierarchy` annotation, either on an individual test class |
|
or within a test class hierarchy. If a context hierarchy is declared on multiple classes |
|
within a test class hierarchy, you can also merge or override the context configuration |
|
for a specific, named level in the context hierarchy. When merging configuration for a |
|
given level in the hierarchy, the configuration resource type (that is, XML configuration |
|
files or component classes) must be consistent. Otherwise, it is perfectly acceptable to |
|
have different levels in a context hierarchy configured using different resource types. |
|
|
|
The remaining JUnit Jupiter based examples in this section show common configuration |
|
scenarios for integration tests that require the use of context hierarchies. |
|
|
|
**Single test class with context hierarchy** |
|
-- |
|
`ControllerIntegrationTests` represents a typical integration testing scenario for a |
|
Spring MVC web application by declaring a context hierarchy that consists of two levels, |
|
one for the root `WebApplicationContext` (loaded by using the `TestAppConfig` |
|
`@Configuration` class) and one for the dispatcher servlet `WebApplicationContext` |
|
(loaded by using the `WebConfig` `@Configuration` class). The `WebApplicationContext` |
|
that is autowired into the test instance is the one for the child context (that is, the |
|
lowest context in the hierarchy). The following listing shows this configuration scenario: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
@WebAppConfiguration |
|
@ContextHierarchy({ |
|
@ContextConfiguration(classes = TestAppConfig.class), |
|
@ContextConfiguration(classes = WebConfig.class) |
|
}) |
|
class ControllerIntegrationTests { |
|
|
|
@Autowired |
|
WebApplicationContext wac; |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
@WebAppConfiguration |
|
@ContextHierarchy( |
|
ContextConfiguration(classes = [TestAppConfig::class]), |
|
ContextConfiguration(classes = [WebConfig::class])) |
|
class ControllerIntegrationTests { |
|
|
|
@Autowired |
|
lateinit var wac: WebApplicationContext |
|
|
|
// ... |
|
} |
|
---- |
|
-- |
|
|
|
**Class hierarchy with implicit parent context** |
|
-- |
|
The test classes in this example define a context hierarchy within a test class |
|
hierarchy. `AbstractWebTests` declares the configuration for a root |
|
`WebApplicationContext` in a Spring-powered web application. Note, however, that |
|
`AbstractWebTests` does not declare `@ContextHierarchy`. Consequently, subclasses of |
|
`AbstractWebTests` can optionally participate in a context hierarchy or follow the |
|
standard semantics for `@ContextConfiguration`. `SoapWebServiceTests` and |
|
`RestWebServiceTests` both extend `AbstractWebTests` and define a context hierarchy by |
|
using `@ContextHierarchy`. The result is that three application contexts are loaded (one |
|
for each declaration of `@ContextConfiguration`), and the application context loaded |
|
based on the configuration in `AbstractWebTests` is set as the parent context for each of |
|
the contexts loaded for the concrete subclasses. The following listing shows this |
|
configuration scenario: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
@WebAppConfiguration |
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") |
|
public abstract class AbstractWebTests {} |
|
|
|
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml")) |
|
public class SoapWebServiceTests extends AbstractWebTests {} |
|
|
|
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml")) |
|
public class RestWebServiceTests extends AbstractWebTests {} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
@WebAppConfiguration |
|
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml") |
|
abstract class AbstractWebTests |
|
|
|
@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml")) |
|
class SoapWebServiceTests : AbstractWebTests() |
|
|
|
@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml")) |
|
class RestWebServiceTests : AbstractWebTests() |
|
|
|
---- |
|
-- |
|
|
|
**Class hierarchy with merged context hierarchy configuration** |
|
-- |
|
The classes in this example show the use of named hierarchy levels in order to merge the |
|
configuration for specific levels in a context hierarchy. `BaseTests` defines two levels |
|
in the hierarchy, `parent` and `child`. `ExtendedTests` extends `BaseTests` and instructs |
|
the Spring TestContext Framework to merge the context configuration for the `child` |
|
hierarchy level, by ensuring that the names declared in the `name` attribute in |
|
`@ContextConfiguration` are both `child`. The result is that three application contexts |
|
are loaded: one for `/app-config.xml`, one for `/user-config.xml`, and one for |
|
`{"/user-config.xml", "/order-config.xml"}`. As with the previous example, the |
|
application context loaded from `/app-config.xml` is set as the parent context for the |
|
contexts loaded from `/user-config.xml` and `{"/user-config.xml", "/order-config.xml"}`. |
|
The following listing shows this configuration scenario: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
@ContextHierarchy({ |
|
@ContextConfiguration(name = "parent", locations = "/app-config.xml"), |
|
@ContextConfiguration(name = "child", locations = "/user-config.xml") |
|
}) |
|
class BaseTests {} |
|
|
|
@ContextHierarchy( |
|
@ContextConfiguration(name = "child", locations = "/order-config.xml") |
|
) |
|
class ExtendedTests extends BaseTests {} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
@ContextHierarchy( |
|
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]), |
|
ContextConfiguration(name = "child", locations = ["/user-config.xml"])) |
|
open class BaseTests {} |
|
|
|
@ContextHierarchy( |
|
ContextConfiguration(name = "child", locations = ["/order-config.xml"]) |
|
) |
|
class ExtendedTests : BaseTests() {} |
|
---- |
|
-- |
|
|
|
**Class hierarchy with overridden context hierarchy configuration** |
|
-- |
|
In contrast to the previous example, this example demonstrates how to override the |
|
configuration for a given named level in a context hierarchy by setting the |
|
`inheritLocations` flag in `@ContextConfiguration` to `false`. Consequently, the |
|
application context for `ExtendedTests` is loaded only from `/test-user-config.xml` and |
|
has its parent set to the context loaded from `/app-config.xml`. The following listing |
|
shows this configuration scenario: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
@ContextHierarchy({ |
|
@ContextConfiguration(name = "parent", locations = "/app-config.xml"), |
|
@ContextConfiguration(name = "child", locations = "/user-config.xml") |
|
}) |
|
class BaseTests {} |
|
|
|
@ContextHierarchy( |
|
@ContextConfiguration( |
|
name = "child", |
|
locations = "/test-user-config.xml", |
|
inheritLocations = false |
|
)) |
|
class ExtendedTests extends BaseTests {} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
@ContextHierarchy( |
|
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]), |
|
ContextConfiguration(name = "child", locations = ["/user-config.xml"])) |
|
open class BaseTests {} |
|
|
|
@ContextHierarchy( |
|
ContextConfiguration( |
|
name = "child", |
|
locations = ["/test-user-config.xml"], |
|
inheritLocations = false |
|
)) |
|
class ExtendedTests : BaseTests() {} |
|
---- |
|
|
|
.Dirtying a context within a context hierarchy |
|
NOTE: If you use `@DirtiesContext` in a test whose context is configured as part of a |
|
context hierarchy, you can use the `hierarchyMode` flag to control how the context cache |
|
is cleared. For further details, see the discussion of `@DirtiesContext` in |
|
<<spring-testing-annotation-dirtiescontext, Spring Testing Annotations>> and the |
|
{api-spring-framework}/test/annotation/DirtiesContext.html[`@DirtiesContext`] javadoc. |
|
-- |
|
|
|
[[testcontext-fixture-di]] |
|
== Dependency Injection of Test Fixtures |
|
|
|
When you use the `DependencyInjectionTestExecutionListener` (which is configured by |
|
default), the dependencies of your test instances are injected from beans in the |
|
application context that you configured with `@ContextConfiguration` or related |
|
annotations. You may use setter injection, field injection, or both, depending on |
|
which annotations you choose and whether you place them on setter methods or fields. |
|
If you are using JUnit Jupiter you may also optionally use constructor injection |
|
(see <<testcontext-junit-jupiter-di>>). For consistency with Spring's annotation-based |
|
injection support, you may also use Spring's `@Autowired` annotation or the `@Inject` |
|
annotation from JSR-330 for field and setter injection. |
|
|
|
TIP: For testing frameworks other than JUnit Jupiter, the TestContext framework does not |
|
participate in instantiation of the test class. Thus, the use of `@Autowired` or |
|
`@Inject` for constructors has no effect for test classes. |
|
|
|
NOTE: Although field injection is discouraged in production code, field injection is |
|
actually quite natural in test code. The rationale for the difference is that you will |
|
never instantiate your test class directly. Consequently, there is no need to be able to |
|
invoke a `public` constructor or setter method on your test class. |
|
|
|
Because `@Autowired` is used to perform <<core.adoc#beans-factory-autowire, autowiring by |
|
type>>, if you have multiple bean definitions of the same type, you cannot rely on this |
|
approach for those particular beans. In that case, you can use `@Autowired` in |
|
conjunction with `@Qualifier`. You can also choose to use `@Inject` in conjunction with |
|
`@Named`. Alternatively, if your test class has access to its `ApplicationContext`, you |
|
can perform an explicit lookup by using (for example) a call to |
|
`applicationContext.getBean("titleRepository", TitleRepository.class)`. |
|
|
|
If you do not want dependency injection applied to your test instances, do not annotate |
|
fields or setter methods with `@Autowired` or `@Inject`. Alternatively, you can disable |
|
dependency injection altogether by explicitly configuring your class with |
|
`@TestExecutionListeners` and omitting `DependencyInjectionTestExecutionListener.class` |
|
from the list of listeners. |
|
|
|
Consider the scenario of testing a `HibernateTitleRepository` class, as outlined in the |
|
<<integration-testing-goals, Goals>> section. The next two code listings demonstrate the |
|
use of `@Autowired` on fields and setter methods. The application context configuration |
|
is presented after all sample code listings. |
|
|
|
[NOTE] |
|
==== |
|
The dependency injection behavior in the following code listings is not specific to JUnit |
|
Jupiter. The same DI techniques can be used in conjunction with any supported testing |
|
framework. |
|
|
|
The following examples make calls to static assertion methods, such as `assertNotNull()`, |
|
but without prepending the call with `Assertions`. In such cases, assume that the method |
|
was properly imported through an `import static` declaration that is not shown in the |
|
example. |
|
==== |
|
|
|
The first code listing shows a JUnit Jupiter based implementation of the test class that |
|
uses `@Autowired` for field injection: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// specifies the Spring configuration to load for this test fixture |
|
@ContextConfiguration("repository-config.xml") |
|
class HibernateTitleRepositoryTests { |
|
|
|
// this instance will be dependency injected by type |
|
@Autowired |
|
HibernateTitleRepository titleRepository; |
|
|
|
@Test |
|
void findById() { |
|
Title title = titleRepository.findById(new Long(10)); |
|
assertNotNull(title); |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// specifies the Spring configuration to load for this test fixture |
|
@ContextConfiguration("repository-config.xml") |
|
class HibernateTitleRepositoryTests { |
|
|
|
// this instance will be dependency injected by type |
|
@Autowired |
|
lateinit var titleRepository: HibernateTitleRepository |
|
|
|
@Test |
|
fun findById() { |
|
val title = titleRepository.findById(10) |
|
assertNotNull(title) |
|
} |
|
} |
|
---- |
|
|
|
Alternatively, you can configure the class to use `@Autowired` for setter injection, as |
|
follows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExtendWith(SpringExtension.class) |
|
// specifies the Spring configuration to load for this test fixture |
|
@ContextConfiguration("repository-config.xml") |
|
class HibernateTitleRepositoryTests { |
|
|
|
// this instance will be dependency injected by type |
|
HibernateTitleRepository titleRepository; |
|
|
|
@Autowired |
|
void setTitleRepository(HibernateTitleRepository titleRepository) { |
|
this.titleRepository = titleRepository; |
|
} |
|
|
|
@Test |
|
void findById() { |
|
Title title = titleRepository.findById(new Long(10)); |
|
assertNotNull(title); |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExtendWith(SpringExtension::class) |
|
// specifies the Spring configuration to load for this test fixture |
|
@ContextConfiguration("repository-config.xml") |
|
class HibernateTitleRepositoryTests { |
|
|
|
// this instance will be dependency injected by type |
|
lateinit var titleRepository: HibernateTitleRepository |
|
|
|
@Autowired |
|
fun setTitleRepository(titleRepository: HibernateTitleRepository) { |
|
this.titleRepository = titleRepository |
|
} |
|
|
|
@Test |
|
fun findById() { |
|
val title = titleRepository.findById(10) |
|
assertNotNull(title) |
|
} |
|
} |
|
---- |
|
|
|
The preceding code listings use the same XML context file referenced by the |
|
`@ContextConfiguration` annotation (that is, `repository-config.xml`). The following |
|
shows this configuration: |
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation="http://www.springframework.org/schema/beans |
|
https://www.springframework.org/schema/beans/spring-beans.xsd"> |
|
|
|
<!-- this bean will be injected into the HibernateTitleRepositoryTests class --> |
|
<bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository"> |
|
<property name="sessionFactory" ref="sessionFactory"/> |
|
</bean> |
|
|
|
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> |
|
<!-- configuration elided for brevity --> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
===== |
|
If you are extending from a Spring-provided test base class that happens to use |
|
`@Autowired` on one of its setter methods, you might have multiple beans of the affected |
|
type defined in your application context (for example, multiple `DataSource` beans). In |
|
such a case, you can override the setter method and use the `@Qualifier` annotation to |
|
indicate a specific target bean, as follows (but make sure to delegate to the overridden |
|
method in the superclass as well): |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
@Override |
|
public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) { |
|
super.setDataSource(dataSource); |
|
} |
|
|
|
// ... |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
override fun setDataSource(@Qualifier("myDataSource") dataSource: DataSource) { |
|
super.setDataSource(dataSource) |
|
} |
|
|
|
// ... |
|
---- |
|
|
|
The specified qualifier value indicates the specific `DataSource` bean to inject, |
|
narrowing the set of type matches to a specific bean. Its value is matched against |
|
`<qualifier>` declarations within the corresponding `<bean>` definitions. The bean name |
|
is used as a fallback qualifier value, so you can effectively also point to a specific |
|
bean by name there (as shown earlier, assuming that `myDataSource` is the bean `id`). |
|
===== |
|
|
|
|
|
[[testcontext-web-scoped-beans]] |
|
== Testing Request- and Session-scoped Beans |
|
|
|
Spring has supported <<core#beans-factory-scopes-other, Request- and session-scoped |
|
beans>> since the early years, and you can test your request-scoped and session-scoped |
|
beans by following these steps: |
|
|
|
* Ensure that a `WebApplicationContext` is loaded for your test by annotating your test |
|
class with `@WebAppConfiguration`. |
|
* Inject the mock request or session into your test instance and prepare your test |
|
fixture as appropriate. |
|
* Invoke your web component that you retrieved from the configured |
|
`WebApplicationContext` (with dependency injection). |
|
* Perform assertions against the mocks. |
|
|
|
The next code snippet shows the XML configuration for a login use case. Note that the |
|
`userService` bean has a dependency on a request-scoped `loginAction` bean. Also, the |
|
`LoginAction` is instantiated by using <<core.adoc#expressions, SpEL expressions>> that |
|
retrieve the username and password from the current HTTP request. In our test, we want to |
|
configure these request parameters through the mock managed by the TestContext framework. |
|
The following listing shows the configuration for this use case: |
|
|
|
.Request-scoped bean configuration |
|
[source,xml,indent=0] |
|
---- |
|
<beans> |
|
|
|
<bean id="userService" class="com.example.SimpleUserService" |
|
c:loginAction-ref="loginAction"/> |
|
|
|
<bean id="loginAction" class="com.example.LoginAction" |
|
c:username="#{request.getParameter('user')}" |
|
c:password="#{request.getParameter('pswd')}" |
|
scope="request"> |
|
<aop:scoped-proxy/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
In `RequestScopedBeanTests`, we inject both the `UserService` (that is, the subject under |
|
test) and the `MockHttpServletRequest` into our test instance. Within our |
|
`requestScope()` test method, we set up our test fixture by setting request parameters in |
|
the provided `MockHttpServletRequest`. When the `loginUser()` method is invoked on our |
|
`userService`, we are assured that the user service has access to the request-scoped |
|
`loginAction` for the current `MockHttpServletRequest` (that is, the one in which we just |
|
set parameters). We can then perform assertions against the results based on the known |
|
inputs for the username and password. The following listing shows how to do so: |
|
|
|
.Request-scoped bean test |
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitWebConfig |
|
class RequestScopedBeanTests { |
|
|
|
@Autowired UserService userService; |
|
@Autowired MockHttpServletRequest request; |
|
|
|
@Test |
|
void requestScope() { |
|
request.setParameter("user", "enigma"); |
|
request.setParameter("pswd", "$pr!ng"); |
|
|
|
LoginResults results = userService.loginUser(); |
|
// assert results |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitWebConfig |
|
class RequestScopedBeanTests { |
|
|
|
@Autowired lateinit var userService: UserService |
|
@Autowired lateinit var request: MockHttpServletRequest |
|
|
|
@Test |
|
fun requestScope() { |
|
request.setParameter("user", "enigma") |
|
request.setParameter("pswd", "\$pr!ng") |
|
|
|
val results = userService.loginUser() |
|
// assert results |
|
} |
|
} |
|
---- |
|
|
|
The following code snippet is similar to the one we saw earlier for a request-scoped |
|
bean. However, this time, the `userService` bean has a dependency on a session-scoped |
|
`userPreferences` bean. Note that the `UserPreferences` bean is instantiated by using a |
|
SpEL expression that retrieves the theme from the current HTTP session. In our test, we |
|
need to configure a theme in the mock session managed by the TestContext framework. The |
|
following example shows how to do so: |
|
|
|
.Session-scoped bean configuration |
|
[source,xml,indent=0,subs="verbatim,quotes"] |
|
---- |
|
<beans> |
|
|
|
<bean id="userService" class="com.example.SimpleUserService" |
|
c:userPreferences-ref="userPreferences" /> |
|
|
|
<bean id="userPreferences" class="com.example.UserPreferences" |
|
c:theme="#{session.getAttribute('theme')}" |
|
scope="session"> |
|
<aop:scoped-proxy/> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
In `SessionScopedBeanTests`, we inject the `UserService` and the `MockHttpSession` into |
|
our test instance. Within our `sessionScope()` test method, we set up our test fixture by |
|
setting the expected `theme` attribute in the provided `MockHttpSession`. When the |
|
`processUserPreferences()` method is invoked on our `userService`, we are assured that |
|
the user service has access to the session-scoped `userPreferences` for the current |
|
`MockHttpSession`, and we can perform assertions against the results based on the |
|
configured theme. The following example shows how to do so: |
|
|
|
.Session-scoped bean test |
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitWebConfig |
|
class SessionScopedBeanTests { |
|
|
|
@Autowired UserService userService; |
|
@Autowired MockHttpSession session; |
|
|
|
@Test |
|
void sessionScope() throws Exception { |
|
session.setAttribute("theme", "blue"); |
|
|
|
Results results = userService.processUserPreferences(); |
|
// assert results |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitWebConfig |
|
class SessionScopedBeanTests { |
|
|
|
@Autowired lateinit var userService: UserService |
|
@Autowired lateinit var session: MockHttpSession |
|
|
|
@Test |
|
fun sessionScope() { |
|
session.setAttribute("theme", "blue") |
|
|
|
val results = userService.processUserPreferences() |
|
// assert results |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-tx]] |
|
== Transaction Management |
|
|
|
In the TestContext framework, transactions are managed by the |
|
`TransactionalTestExecutionListener`, which is configured by default, even if you do not |
|
explicitly declare `@TestExecutionListeners` on your test class. To enable support for |
|
transactions, however, you must configure a `PlatformTransactionManager` bean in the |
|
`ApplicationContext` that is loaded with `@ContextConfiguration` semantics (further |
|
details are provided later). In addition, you must declare Spring's `@Transactional` |
|
annotation either at the class or the method level for your tests. |
|
|
|
[[testcontext-tx-test-managed-transactions]] |
|
=== Test-managed Transactions |
|
|
|
Test-managed transactions are transactions that are managed declaratively by using the |
|
`TransactionalTestExecutionListener` or programmatically by using `TestTransaction` |
|
(described later). You should not confuse such transactions with Spring-managed |
|
transactions (those managed directly by Spring within the `ApplicationContext` loaded for |
|
tests) or application-managed transactions (those managed programmatically within |
|
application code that is invoked by tests). Spring-managed and application-managed |
|
transactions typically participate in test-managed transactions. However, you should use |
|
caution if Spring-managed or application-managed transactions are configured with any |
|
propagation type other than `REQUIRED` or `SUPPORTS` (see the discussion on |
|
<<data-access.adoc#tx-propagation, transaction propagation>> for details). |
|
|
|
.Preemptive timeouts and test-managed transactions |
|
[WARNING] |
|
==== |
|
Caution must be taken when using any form of preemptive timeouts from a testing framework |
|
in conjunction with Spring's test-managed transactions. |
|
|
|
Specifically, Spring’s testing support binds transaction state to the current thread (via |
|
a `java.lang.ThreadLocal` variable) _before_ the current test method is invoked. If a |
|
testing framework invokes the current test method in a new thread in order to support a |
|
preemptive timeout, any actions performed within the current test method will _not_ be |
|
invoked within the test-managed transaction. Consequently, the result of any such actions |
|
will not be rolled back with the test-managed transaction. On the contrary, such actions |
|
will be committed to the persistent store -- for example, a relational database -- even |
|
though the test-managed transaction is properly rolled back by Spring. |
|
|
|
Situations in which this can occur include but are not limited to the following. |
|
|
|
* JUnit 4's `@Test(timeout = ...)` support and `TimeOut` rule |
|
* JUnit Jupiter's `assertTimeoutPreemptively(...)` methods in the |
|
`org.junit.jupiter.api.Assertions` class |
|
* TestNG's `@Test(timeOut = ...)` support |
|
==== |
|
|
|
[[testcontext-tx-enabling-transactions]] |
|
=== Enabling and Disabling Transactions |
|
|
|
Annotating a test method with `@Transactional` causes the test to be run within a |
|
transaction that is, by default, automatically rolled back after completion of the test. |
|
If a test class is annotated with `@Transactional`, each test method within that class |
|
hierarchy runs within a transaction. Test methods that are not annotated with |
|
`@Transactional` (at the class or method level) are not run within a transaction. Note |
|
that `@Transactional` is not supported on test lifecycle methods — for example, methods |
|
annotated with JUnit Jupiter's `@BeforeAll`, `@BeforeEach`, etc. Furthermore, tests that |
|
are annotated with `@Transactional` but have the `propagation` attribute set to |
|
`NOT_SUPPORTED` or `NEVER` are not run within a transaction. |
|
|
|
[[testcontext-tx-attribute-support]] |
|
.`@Transactional` attribute support |
|
|=== |
|
|Attribute |Supported for test-managed transactions |
|
|
|
|`value` and `transactionManager` |yes |
|
|
|
|`propagation` |only `Propagation.NOT_SUPPORTED` and `Propagation.NEVER` are supported |
|
|
|
|`isolation` |no |
|
|
|
|`timeout` |no |
|
|
|
|`readOnly` |no |
|
|
|
|`rollbackFor` and `rollbackForClassName` |no: use `TestTransaction.flagForRollback()` instead |
|
|
|
|`noRollbackFor` and `noRollbackForClassName` |no: use `TestTransaction.flagForCommit()` instead |
|
|=== |
|
|
|
[TIP] |
|
==== |
|
Method-level lifecycle methods — for example, methods annotated with JUnit Jupiter's |
|
`@BeforeEach` or `@AfterEach` — are run within a test-managed transaction. On the other |
|
hand, suite-level and class-level lifecycle methods — for example, methods annotated with |
|
JUnit Jupiter's `@BeforeAll` or `@AfterAll` and methods annotated with TestNG's |
|
`@BeforeSuite`, `@AfterSuite`, `@BeforeClass`, or `@AfterClass` — are _not_ run within a |
|
test-managed transaction. |
|
|
|
If you need to run code in a suite-level or class-level lifecycle method within a |
|
transaction, you may wish to inject a corresponding `PlatformTransactionManager` into |
|
your test class and then use that with a `TransactionTemplate` for programmatic |
|
transaction management. |
|
==== |
|
|
|
Note that <<testcontext-support-classes-junit4, |
|
`AbstractTransactionalJUnit4SpringContextTests`>> and |
|
<<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>> |
|
are preconfigured for transactional support at the class level. |
|
|
|
The following example demonstrates a common scenario for writing an integration test for |
|
a Hibernate-based `UserRepository`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestConfig.class) |
|
@Transactional |
|
class HibernateUserRepositoryTests { |
|
|
|
@Autowired |
|
HibernateUserRepository repository; |
|
|
|
@Autowired |
|
SessionFactory sessionFactory; |
|
|
|
JdbcTemplate jdbcTemplate; |
|
|
|
@Autowired |
|
void setDataSource(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
@Test |
|
void createUser() { |
|
// track initial state in test database: |
|
final int count = countRowsInTable("user"); |
|
|
|
User user = new User(...); |
|
repository.save(user); |
|
|
|
// Manual flush is required to avoid false positive in test |
|
sessionFactory.getCurrentSession().flush(); |
|
assertNumUsers(count + 1); |
|
} |
|
|
|
private int countRowsInTable(String tableName) { |
|
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); |
|
} |
|
|
|
private void assertNumUsers(int expected) { |
|
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user")); |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestConfig::class) |
|
@Transactional |
|
class HibernateUserRepositoryTests { |
|
|
|
@Autowired |
|
lateinit var repository: HibernateUserRepository |
|
|
|
@Autowired |
|
lateinit var sessionFactory: SessionFactory |
|
|
|
lateinit var jdbcTemplate: JdbcTemplate |
|
|
|
@Autowired |
|
fun setDataSource(dataSource: DataSource) { |
|
this.jdbcTemplate = JdbcTemplate(dataSource) |
|
} |
|
|
|
@Test |
|
fun createUser() { |
|
// track initial state in test database: |
|
val count = countRowsInTable("user") |
|
|
|
val user = User() |
|
repository.save(user) |
|
|
|
// Manual flush is required to avoid false positive in test |
|
sessionFactory.getCurrentSession().flush() |
|
assertNumUsers(count + 1) |
|
} |
|
|
|
private fun countRowsInTable(tableName: String): Int { |
|
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName) |
|
} |
|
|
|
private fun assertNumUsers(expected: Int) { |
|
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user")) |
|
} |
|
} |
|
---- |
|
|
|
As explained in <<testcontext-tx-rollback-and-commit-behavior>>, there is no need to |
|
clean up the database after the `createUser()` method runs, since any changes made to the |
|
database are automatically rolled back by the `TransactionalTestExecutionListener`. |
|
|
|
[[testcontext-tx-rollback-and-commit-behavior]] |
|
=== Transaction Rollback and Commit Behavior |
|
|
|
By default, test transactions will be automatically rolled back after completion of the |
|
test; however, transactional commit and rollback behavior can be configured declaratively |
|
via the `@Commit` and `@Rollback` annotations. See the corresponding entries in the |
|
<<integration-testing-annotations, annotation support>> section for further details. |
|
|
|
[[testcontext-tx-programmatic-tx-mgt]] |
|
=== Programmatic Transaction Management |
|
|
|
You can interact with test-managed transactions programmatically by using the static |
|
methods in `TestTransaction`. For example, you can use `TestTransaction` within test |
|
methods, before methods, and after methods to start or end the current test-managed |
|
transaction or to configure the current test-managed transaction for rollback or commit. |
|
Support for `TestTransaction` is automatically available whenever the |
|
`TransactionalTestExecutionListener` is enabled. |
|
|
|
The following example demonstrates some of the features of `TestTransaction`. See the |
|
javadoc for {api-spring-framework}/test/context/transaction/TestTransaction.html[`TestTransaction`] |
|
for further details. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ContextConfiguration(classes = TestConfig.class) |
|
public class ProgrammaticTransactionManagementTests extends |
|
AbstractTransactionalJUnit4SpringContextTests { |
|
|
|
@Test |
|
public void transactionalTest() { |
|
// assert initial state in test database: |
|
assertNumUsers(2); |
|
|
|
deleteFromTables("user"); |
|
|
|
// changes to the database will be committed! |
|
TestTransaction.flagForCommit(); |
|
TestTransaction.end(); |
|
assertFalse(TestTransaction.isActive()); |
|
assertNumUsers(0); |
|
|
|
TestTransaction.start(); |
|
// perform other actions against the database that will |
|
// be automatically rolled back after the test completes... |
|
} |
|
|
|
protected void assertNumUsers(int expected) { |
|
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user")); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ContextConfiguration(classes = [TestConfig::class]) |
|
class ProgrammaticTransactionManagementTests : AbstractTransactionalJUnit4SpringContextTests() { |
|
|
|
@Test |
|
fun transactionalTest() { |
|
// assert initial state in test database: |
|
assertNumUsers(2) |
|
|
|
deleteFromTables("user") |
|
|
|
// changes to the database will be committed! |
|
TestTransaction.flagForCommit() |
|
TestTransaction.end() |
|
assertFalse(TestTransaction.isActive()) |
|
assertNumUsers(0) |
|
|
|
TestTransaction.start() |
|
// perform other actions against the database that will |
|
// be automatically rolled back after the test completes... |
|
} |
|
|
|
protected fun assertNumUsers(expected: Int) { |
|
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user")) |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-tx-before-and-after-tx]] |
|
=== Running Code Outside of a Transaction |
|
|
|
Occasionally, you may need to run certain code before or after a transactional test |
|
method but outside the transactional context -- for example, to verify the initial |
|
database state prior to running your test or to verify expected transactional commit |
|
behavior after your test runs (if the test was configured to commit the transaction). |
|
`TransactionalTestExecutionListener` supports the `@BeforeTransaction` and |
|
`@AfterTransaction` annotations for exactly such scenarios. You can annotate any `void` |
|
method in a test class or any `void` default method in a test interface with one of these |
|
annotations, and the `TransactionalTestExecutionListener` ensures that your before |
|
transaction method or after transaction method runs at the appropriate time. |
|
|
|
TIP: Any before methods (such as methods annotated with JUnit Jupiter's `@BeforeEach`) |
|
and any after methods (such as methods annotated with JUnit Jupiter's `@AfterEach`) are |
|
run within a transaction. In addition, methods annotated with `@BeforeTransaction` or |
|
`@AfterTransaction` are not run for test methods that are not configured to run within a |
|
transaction. |
|
|
|
[[testcontext-tx-mgr-config]] |
|
=== Configuring a Transaction Manager |
|
|
|
`TransactionalTestExecutionListener` expects a `PlatformTransactionManager` bean to be |
|
defined in the Spring `ApplicationContext` for the test. If there are multiple instances |
|
of `PlatformTransactionManager` within the test's `ApplicationContext`, you can declare a |
|
qualifier by using `@Transactional("myTxMgr")` or `@Transactional(transactionManager = |
|
"myTxMgr")`, or `TransactionManagementConfigurer` can be implemented by an |
|
`@Configuration` class. Consult the |
|
{api-spring-framework}/test/context/transaction/TestContextTransactionUtils.html#retrieveTransactionManager-org.springframework.test.context.TestContext-java.lang.String-[javadoc |
|
for `TestContextTransactionUtils.retrieveTransactionManager()`] for details on the |
|
algorithm used to look up a transaction manager in the test's `ApplicationContext`. |
|
|
|
[[testcontext-tx-annotation-demo]] |
|
=== Demonstration of All Transaction-related Annotations |
|
|
|
The following JUnit Jupiter based example displays a fictitious integration testing |
|
scenario that highlights all transaction-related annotations. The example is not intended |
|
to demonstrate best practices but rather to demonstrate how these annotations can be |
|
used. See the <<integration-testing-annotations, annotation support>> section for further |
|
information and configuration examples. <<testcontext-executing-sql-declaratively-tx, |
|
Transaction management for `@Sql`>> contains an additional example that uses `@Sql` for |
|
declarative SQL script execution with default transaction rollback semantics. The |
|
following example shows the relevant annotations: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig |
|
@Transactional(transactionManager = "txMgr") |
|
@Commit |
|
class FictitiousTransactionalTest { |
|
|
|
@BeforeTransaction |
|
void verifyInitialDatabaseState() { |
|
// logic to verify the initial state before a transaction is started |
|
} |
|
|
|
@BeforeEach |
|
void setUpTestDataWithinTransaction() { |
|
// set up test data within the transaction |
|
} |
|
|
|
@Test |
|
// overrides the class-level @Commit setting |
|
@Rollback |
|
void modifyDatabaseWithinTransaction() { |
|
// logic which uses the test data and modifies database state |
|
} |
|
|
|
@AfterEach |
|
void tearDownWithinTransaction() { |
|
// run "tear down" logic within the transaction |
|
} |
|
|
|
@AfterTransaction |
|
void verifyFinalDatabaseState() { |
|
// logic to verify the final state after transaction has rolled back |
|
} |
|
|
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig |
|
@Transactional(transactionManager = "txMgr") |
|
@Commit |
|
class FictitiousTransactionalTest { |
|
|
|
@BeforeTransaction |
|
fun verifyInitialDatabaseState() { |
|
// logic to verify the initial state before a transaction is started |
|
} |
|
|
|
@BeforeEach |
|
fun setUpTestDataWithinTransaction() { |
|
// set up test data within the transaction |
|
} |
|
|
|
@Test |
|
// overrides the class-level @Commit setting |
|
@Rollback |
|
fun modifyDatabaseWithinTransaction() { |
|
// logic which uses the test data and modifies database state |
|
} |
|
|
|
@AfterEach |
|
fun tearDownWithinTransaction() { |
|
// run "tear down" logic within the transaction |
|
} |
|
|
|
@AfterTransaction |
|
fun verifyFinalDatabaseState() { |
|
// logic to verify the final state after transaction has rolled back |
|
} |
|
|
|
} |
|
---- |
|
|
|
[[testcontext-tx-false-positives]] |
|
.Avoid false positives when testing ORM code |
|
[NOTE] |
|
===== |
|
When you test application code that manipulates the state of a Hibernate session or JPA |
|
persistence context, make sure to flush the underlying unit of work within test methods |
|
that run that code. Failing to flush the underlying unit of work can produce false |
|
positives: Your test passes, but the same code throws an exception in a live, production |
|
environment. Note that this applies to any ORM framework that maintains an in-memory unit |
|
of work. In the following Hibernate-based example test case, one method demonstrates a |
|
false positive, and the other method correctly exposes the results of flushing the |
|
session: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
SessionFactory sessionFactory; |
|
|
|
@Transactional |
|
@Test // no expected exception! |
|
public void falsePositive() { |
|
updateEntityInHibernateSession(); |
|
// False positive: an exception will be thrown once the Hibernate |
|
// Session is finally flushed (i.e., in production code) |
|
} |
|
|
|
@Transactional |
|
@Test(expected = ...) |
|
public void updateWithSessionFlush() { |
|
updateEntityInHibernateSession(); |
|
// Manual flush is required to avoid false positive in test |
|
sessionFactory.getCurrentSession().flush(); |
|
} |
|
|
|
// ... |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
lateinit var sessionFactory: SessionFactory |
|
|
|
@Transactional |
|
@Test // no expected exception! |
|
fun falsePositive() { |
|
updateEntityInHibernateSession() |
|
// False positive: an exception will be thrown once the Hibernate |
|
// Session is finally flushed (i.e., in production code) |
|
} |
|
|
|
@Transactional |
|
@Test(expected = ...) |
|
fun updateWithSessionFlush() { |
|
updateEntityInHibernateSession() |
|
// Manual flush is required to avoid false positive in test |
|
sessionFactory.getCurrentSession().flush() |
|
} |
|
|
|
// ... |
|
---- |
|
|
|
The following example shows matching methods for JPA: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// ... |
|
|
|
@PersistenceContext |
|
EntityManager entityManager; |
|
|
|
@Transactional |
|
@Test // no expected exception! |
|
public void falsePositive() { |
|
updateEntityInJpaPersistenceContext(); |
|
// False positive: an exception will be thrown once the JPA |
|
// EntityManager is finally flushed (i.e., in production code) |
|
} |
|
|
|
@Transactional |
|
@Test(expected = ...) |
|
public void updateWithEntityManagerFlush() { |
|
updateEntityInJpaPersistenceContext(); |
|
// Manual flush is required to avoid false positive in test |
|
entityManager.flush(); |
|
} |
|
|
|
// ... |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// ... |
|
|
|
@PersistenceContext |
|
lateinit var entityManager:EntityManager |
|
|
|
@Transactional |
|
@Test // no expected exception! |
|
fun falsePositive() { |
|
updateEntityInJpaPersistenceContext() |
|
// False positive: an exception will be thrown once the JPA |
|
// EntityManager is finally flushed (i.e., in production code) |
|
} |
|
|
|
@Transactional |
|
@Test(expected = ...) |
|
void updateWithEntityManagerFlush() { |
|
updateEntityInJpaPersistenceContext() |
|
// Manual flush is required to avoid false positive in test |
|
entityManager.flush() |
|
} |
|
|
|
// ... |
|
---- |
|
===== |
|
|
|
[[testcontext-tx-orm-lifecycle-callbacks]] |
|
.Testing ORM entity lifecycle callbacks |
|
[NOTE] |
|
===== |
|
Similar to the note about avoiding <<testcontext-tx-false-positives, false positives>> |
|
when testing ORM code, if your application makes use of entity lifecycle callbacks (also |
|
known as entity listeners), make sure to flush the underlying unit of work within test |
|
methods that run that code. Failing to _flush_ or _clear_ the underlying unit of work can |
|
result in certain lifecycle callbacks not being invoked. |
|
|
|
For example, when using JPA, `@PostPersist`, `@PreUpdate`, and `@PostUpdate` callbacks |
|
will not be called unless `entityManager.flush()` is invoked after an entity has been |
|
saved or updated. Similarly, if an entity is already attached to the current unit of work |
|
(associated with the current persistence context), an attempt to reload the entity will |
|
not result in a `@PostLoad` callback unless `entityManager.clear()` is invoked before the |
|
attempt to reload the entity. |
|
|
|
The following example shows how to flush the `EntityManager` to ensure that |
|
`@PostPersist` callbacks are invoked when an entity is persisted. An entity listener with |
|
a `@PostPersist` callback method has been registered for the `Person` entity used in the |
|
example. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
JpaPersonRepository repo; |
|
|
|
@PersistenceContext |
|
EntityManager entityManager; |
|
|
|
@Transactional |
|
@Test |
|
void savePerson() { |
|
// EntityManager#persist(...) results in @PrePersist but not @PostPersist |
|
repo.save(new Person("Jane")); |
|
|
|
// Manual flush is required for @PostPersist callback to be invoked |
|
entityManager.flush(); |
|
|
|
// Test code that relies on the @PostPersist callback |
|
// having been invoked... |
|
} |
|
|
|
// ... |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// ... |
|
|
|
@Autowired |
|
lateinit var repo: JpaPersonRepository |
|
|
|
@PersistenceContext |
|
lateinit var entityManager: EntityManager |
|
|
|
@Transactional |
|
@Test |
|
fun savePerson() { |
|
// EntityManager#persist(...) results in @PrePersist but not @PostPersist |
|
repo.save(Person("Jane")) |
|
|
|
// Manual flush is required for @PostPersist callback to be invoked |
|
entityManager.flush() |
|
|
|
// Test code that relies on the @PostPersist callback |
|
// having been invoked... |
|
} |
|
|
|
// ... |
|
---- |
|
|
|
See |
|
https://github.com/spring-projects/spring-framework/blob/main/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/orm/JpaEntityListenerTests.java[JpaEntityListenerTests] |
|
in the Spring Framework test suite for working examples using all JPA lifecycle callbacks. |
|
===== |
|
|
|
|
|
[[testcontext-executing-sql]] |
|
== Executing SQL Scripts |
|
|
|
When writing integration tests against a relational database, it is often beneficial to |
|
run SQL scripts to modify the database schema or insert test data into tables. The |
|
`spring-jdbc` module provides support for _initializing_ an embedded or existing database |
|
by executing SQL scripts when the Spring `ApplicationContext` is loaded. See |
|
<<data-access.adoc#jdbc-embedded-database-support, Embedded database support>> and |
|
<<data-access.adoc#jdbc-embedded-database-dao-testing, Testing data access logic with an |
|
embedded database>> for details. |
|
|
|
Although it is very useful to initialize a database for testing _once_ when the |
|
`ApplicationContext` is loaded, sometimes it is essential to be able to modify the |
|
database _during_ integration tests. The following sections explain how to run SQL |
|
scripts programmatically and declaratively during integration tests. |
|
|
|
[[testcontext-executing-sql-programmatically]] |
|
=== Executing SQL scripts programmatically |
|
|
|
Spring provides the following options for executing SQL scripts programmatically within |
|
integration test methods. |
|
|
|
* `org.springframework.jdbc.datasource.init.ScriptUtils` |
|
* `org.springframework.jdbc.datasource.init.ResourceDatabasePopulator` |
|
* `org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests` |
|
* `org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests` |
|
|
|
`ScriptUtils` provides a collection of static utility methods for working with SQL |
|
scripts and is mainly intended for internal use within the framework. However, if you |
|
require full control over how SQL scripts are parsed and run, `ScriptUtils` may suit |
|
your needs better than some of the other alternatives described later. See the |
|
{api-spring-framework}/jdbc/datasource/init/ScriptUtils.html[javadoc] for individual |
|
methods in `ScriptUtils` for further details. |
|
|
|
`ResourceDatabasePopulator` provides an object-based API for programmatically populating, |
|
initializing, or cleaning up a database by using SQL scripts defined in external |
|
resources. `ResourceDatabasePopulator` provides options for configuring the character |
|
encoding, statement separator, comment delimiters, and error handling flags used when |
|
parsing and running the scripts. Each of the configuration options has a reasonable |
|
default value. See the |
|
{api-spring-framework}/jdbc/datasource/init/ResourceDatabasePopulator.html[javadoc] for |
|
details on default values. To run the scripts configured in a |
|
`ResourceDatabasePopulator`, you can invoke either the `populate(Connection)` method to |
|
run the populator against a `java.sql.Connection` or the `execute(DataSource)` method |
|
to run the populator against a `javax.sql.DataSource`. The following example |
|
specifies SQL scripts for a test schema and test data, sets the statement separator to |
|
`@@`, and run the scripts against a `DataSource`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Test |
|
void databaseTest() { |
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); |
|
populator.addScripts( |
|
new ClassPathResource("test-schema.sql"), |
|
new ClassPathResource("test-data.sql")); |
|
populator.setSeparator("@@"); |
|
populator.execute(this.dataSource); |
|
// run code that uses the test schema and data |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Test |
|
fun databaseTest() { |
|
val populator = ResourceDatabasePopulator() |
|
populator.addScripts( |
|
ClassPathResource("test-schema.sql"), |
|
ClassPathResource("test-data.sql")) |
|
populator.setSeparator("@@") |
|
populator.execute(dataSource) |
|
// run code that uses the test schema and data |
|
} |
|
---- |
|
|
|
Note that `ResourceDatabasePopulator` internally delegates to `ScriptUtils` for parsing |
|
and running SQL scripts. Similarly, the `executeSqlScript(..)` methods in |
|
<<testcontext-support-classes-junit4, `AbstractTransactionalJUnit4SpringContextTests`>> |
|
and <<testcontext-support-classes-testng, `AbstractTransactionalTestNGSpringContextTests`>> |
|
internally use a `ResourceDatabasePopulator` to run SQL scripts. See the Javadoc for the |
|
various `executeSqlScript(..)` methods for further details. |
|
|
|
[[testcontext-executing-sql-declaratively]] |
|
=== Executing SQL scripts declaratively with @Sql |
|
|
|
In addition to the aforementioned mechanisms for running SQL scripts programmatically, |
|
you can declaratively configure SQL scripts in the Spring TestContext Framework. |
|
Specifically, you can declare the `@Sql` annotation on a test class or test method to |
|
configure individual SQL statements or the resource paths to SQL scripts that should be |
|
run against a given database before or after an integration test method. Support for |
|
`@Sql` is provided by the `SqlScriptsTestExecutionListener`, which is enabled by default. |
|
|
|
NOTE: Method-level `@Sql` declarations override class-level declarations by default. As |
|
of Spring Framework 5.2, however, this behavior may be configured per test class or per |
|
test method via `@SqlMergeMode`. See |
|
<<testcontext-executing-sql-declaratively-script-merging>> for further details. |
|
|
|
[[testcontext-executing-sql-declaratively-script-resources]] |
|
==== Path Resource Semantics |
|
|
|
Each path is interpreted as a Spring `Resource`. A plain path (for example, |
|
`"schema.sql"`) is treated as a classpath resource that is relative to the package in |
|
which the test class is defined. A path starting with a slash is treated as an absolute |
|
classpath resource (for example, `"/org/example/schema.sql"`). A path that references a |
|
URL (for example, a path prefixed with `classpath:`, `file:`, `http:`) is loaded by using |
|
the specified resource protocol. |
|
|
|
The following example shows how to use `@Sql` at the class level and at the method level |
|
within a JUnit Jupiter based integration test class: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig |
|
@Sql("/test-schema.sql") |
|
class DatabaseTests { |
|
|
|
@Test |
|
void emptySchemaTest() { |
|
// run code that uses the test schema without any test data |
|
} |
|
|
|
@Test |
|
@Sql({"/test-schema.sql", "/test-user-data.sql"}) |
|
void userTest() { |
|
// run code that uses the test schema and test data |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig |
|
@Sql("/test-schema.sql") |
|
class DatabaseTests { |
|
|
|
@Test |
|
fun emptySchemaTest() { |
|
// run code that uses the test schema without any test data |
|
} |
|
|
|
@Test |
|
@Sql("/test-schema.sql", "/test-user-data.sql") |
|
fun userTest() { |
|
// run code that uses the test schema and test data |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-executing-sql-declaratively-script-detection]] |
|
==== Default Script Detection |
|
|
|
If no SQL scripts or statements are specified, an attempt is made to detect a `default` |
|
script, depending on where `@Sql` is declared. If a default cannot be detected, an |
|
`IllegalStateException` is thrown. |
|
|
|
* Class-level declaration: If the annotated test class is `com.example.MyTest`, the |
|
corresponding default script is `classpath:com/example/MyTest.sql`. |
|
* Method-level declaration: If the annotated test method is named `testMethod()` and is |
|
defined in the class `com.example.MyTest`, the corresponding default script is |
|
`classpath:com/example/MyTest.testMethod.sql`. |
|
|
|
[[testcontext-executing-sql-declaratively-multiple-annotations]] |
|
==== Declaring Multiple `@Sql` Sets |
|
|
|
If you need to configure multiple sets of SQL scripts for a given test class or test |
|
method but with different syntax configuration, different error handling rules, or |
|
different execution phases per set, you can declare multiple instances of `@Sql`. With |
|
Java 8, you can use `@Sql` as a repeatable annotation. Otherwise, you can use the |
|
`@SqlGroup` annotation as an explicit container for declaring multiple instances of |
|
`@Sql`. |
|
|
|
The following example shows how to use `@Sql` as a repeatable annotation with Java 8: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Test |
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")) |
|
@Sql("/test-user-data.sql") |
|
void userTest() { |
|
// run code that uses the test schema and test data |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Repeatable annotations with non-SOURCE retention are not yet supported by Kotlin |
|
---- |
|
|
|
In the scenario presented in the preceding example, the `test-schema.sql` script uses a |
|
different syntax for single-line comments. |
|
|
|
The following example is identical to the preceding example, except that the `@Sql` |
|
declarations are grouped together within `@SqlGroup`. With Java 8 and above, the use of |
|
`@SqlGroup` is optional, but you may need to use `@SqlGroup` for compatibility with |
|
other JVM languages such as Kotlin. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Test |
|
@SqlGroup({ |
|
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")), |
|
@Sql("/test-user-data.sql") |
|
)} |
|
void userTest() { |
|
// run code that uses the test schema and test data |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Test |
|
@SqlGroup( |
|
Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`")), |
|
Sql("/test-user-data.sql")) |
|
fun userTest() { |
|
// Run code that uses the test schema and test data |
|
} |
|
---- |
|
|
|
[[testcontext-executing-sql-declaratively-script-execution-phases]] |
|
==== Script Execution Phases |
|
|
|
By default, SQL scripts are run before the corresponding test method. However, if |
|
you need to run a particular set of scripts after the test method (for example, to clean |
|
up database state), you can use the `executionPhase` attribute in `@Sql`, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Test |
|
@Sql( |
|
scripts = "create-test-data.sql", |
|
config = @SqlConfig(transactionMode = ISOLATED) |
|
) |
|
@Sql( |
|
scripts = "delete-test-data.sql", |
|
config = @SqlConfig(transactionMode = ISOLATED), |
|
executionPhase = AFTER_TEST_METHOD |
|
) |
|
void userTest() { |
|
// run code that needs the test data to be committed |
|
// to the database outside of the test's transaction |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Test |
|
@SqlGroup( |
|
Sql("create-test-data.sql", |
|
config = SqlConfig(transactionMode = ISOLATED)), |
|
Sql("delete-test-data.sql", |
|
config = SqlConfig(transactionMode = ISOLATED), |
|
executionPhase = AFTER_TEST_METHOD)) |
|
fun userTest() { |
|
// run code that needs the test data to be committed |
|
// to the database outside of the test's transaction |
|
} |
|
---- |
|
|
|
Note that `ISOLATED` and `AFTER_TEST_METHOD` are statically imported from |
|
`Sql.TransactionMode` and `Sql.ExecutionPhase`, respectively. |
|
|
|
[[testcontext-executing-sql-declaratively-script-configuration]] |
|
==== Script Configuration with `@SqlConfig` |
|
|
|
You can configure script parsing and error handling by using the `@SqlConfig` annotation. |
|
When declared as a class-level annotation on an integration test class, `@SqlConfig` |
|
serves as global configuration for all SQL scripts within the test class hierarchy. When |
|
declared directly by using the `config` attribute of the `@Sql` annotation, `@SqlConfig` |
|
serves as local configuration for the SQL scripts declared within the enclosing `@Sql` |
|
annotation. Every attribute in `@SqlConfig` has an implicit default value, which is |
|
documented in the javadoc of the corresponding attribute. Due to the rules defined for |
|
annotation attributes in the Java Language Specification, it is, unfortunately, not |
|
possible to assign a value of `null` to an annotation attribute. Thus, in order to |
|
support overrides of inherited global configuration, `@SqlConfig` attributes have an |
|
explicit default value of either `""` (for Strings), `{}` (for arrays), or `DEFAULT` (for |
|
enumerations). This approach lets local declarations of `@SqlConfig` selectively override |
|
individual attributes from global declarations of `@SqlConfig` by providing a value other |
|
than `""`, `{}`, or `DEFAULT`. Global `@SqlConfig` attributes are inherited whenever |
|
local `@SqlConfig` attributes do not supply an explicit value other than `""`, `{}`, or |
|
`DEFAULT`. Explicit local configuration, therefore, overrides global configuration. |
|
|
|
The configuration options provided by `@Sql` and `@SqlConfig` are equivalent to those |
|
supported by `ScriptUtils` and `ResourceDatabasePopulator` but are a superset of those |
|
provided by the `<jdbc:initialize-database/>` XML namespace element. See the javadoc of |
|
individual attributes in {api-spring-framework}/test/context/jdbc/Sql.html[`@Sql`] and |
|
{api-spring-framework}/test/context/jdbc/SqlConfig.html[`@SqlConfig`] for details. |
|
|
|
[[testcontext-executing-sql-declaratively-tx]] |
|
*Transaction management for `@Sql`* |
|
|
|
By default, the `SqlScriptsTestExecutionListener` infers the desired transaction |
|
semantics for scripts configured by using `@Sql`. Specifically, SQL scripts are run |
|
without a transaction, within an existing Spring-managed transaction (for example, a |
|
transaction managed by the `TransactionalTestExecutionListener` for a test annotated with |
|
`@Transactional`), or within an isolated transaction, depending on the configured value |
|
of the `transactionMode` attribute in `@SqlConfig` and the presence of a |
|
`PlatformTransactionManager` in the test's `ApplicationContext`. As a bare minimum, |
|
however, a `javax.sql.DataSource` must be present in the test's `ApplicationContext`. |
|
|
|
If the algorithms used by `SqlScriptsTestExecutionListener` to detect a `DataSource` and |
|
`PlatformTransactionManager` and infer the transaction semantics do not suit your needs, |
|
you can specify explicit names by setting the `dataSource` and `transactionManager` |
|
attributes of `@SqlConfig`. Furthermore, you can control the transaction propagation |
|
behavior by setting the `transactionMode` attribute of `@SqlConfig` (for example, whether |
|
scripts should be run in an isolated transaction). Although a thorough discussion of all |
|
supported options for transaction management with `@Sql` is beyond the scope of this |
|
reference manual, the javadoc for |
|
{api-spring-framework}/test/context/jdbc/SqlConfig.html[`@SqlConfig`] and |
|
{api-spring-framework}/test/context/jdbc/SqlScriptsTestExecutionListener.html[`SqlScriptsTestExecutionListener`] |
|
provide detailed information, and the following example shows a typical testing scenario |
|
that uses JUnit Jupiter and transactional tests with `@Sql`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestDatabaseConfig.class) |
|
@Transactional |
|
class TransactionalSqlScriptsTests { |
|
|
|
final JdbcTemplate jdbcTemplate; |
|
|
|
@Autowired |
|
TransactionalSqlScriptsTests(DataSource dataSource) { |
|
this.jdbcTemplate = new JdbcTemplate(dataSource); |
|
} |
|
|
|
@Test |
|
@Sql("/test-data.sql") |
|
void usersTest() { |
|
// verify state in test database: |
|
assertNumUsers(2); |
|
// run code that uses the test data... |
|
} |
|
|
|
int countRowsInTable(String tableName) { |
|
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); |
|
} |
|
|
|
void assertNumUsers(int expected) { |
|
assertEquals(expected, countRowsInTable("user"), |
|
"Number of rows in the [user] table."); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestDatabaseConfig::class) |
|
@Transactional |
|
class TransactionalSqlScriptsTests @Autowired constructor(dataSource: DataSource) { |
|
|
|
val jdbcTemplate: JdbcTemplate = JdbcTemplate(dataSource) |
|
|
|
@Test |
|
@Sql("/test-data.sql") |
|
fun usersTest() { |
|
// verify state in test database: |
|
assertNumUsers(2) |
|
// run code that uses the test data... |
|
} |
|
|
|
fun countRowsInTable(tableName: String): Int { |
|
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName) |
|
} |
|
|
|
fun assertNumUsers(expected: Int) { |
|
assertEquals(expected, countRowsInTable("user"), |
|
"Number of rows in the [user] table.") |
|
} |
|
} |
|
---- |
|
|
|
Note that there is no need to clean up the database after the `usersTest()` method is |
|
run, since any changes made to the database (either within the test method or within the |
|
`/test-data.sql` script) are automatically rolled back by the |
|
`TransactionalTestExecutionListener` (see <<testcontext-tx,transaction management>> for |
|
details). |
|
|
|
[[testcontext-executing-sql-declaratively-script-merging]] |
|
==== Merging and Overriding Configuration with `@SqlMergeMode` |
|
|
|
As of Spring Framework 5.2, it is possible to merge method-level `@Sql` declarations with |
|
class-level declarations. For example, this allows you to provide the configuration for a |
|
database schema or some common test data once per test class and then provide additional, |
|
use case specific test data per test method. To enable `@Sql` merging, annotate either |
|
your test class or test method with `@SqlMergeMode(MERGE)`. To disable merging for a |
|
specific test method (or specific test subclass), you can switch back to the default mode |
|
via `@SqlMergeMode(OVERRIDE)`. Consult the <<spring-testing-annotation-sqlmergemode, |
|
`@SqlMergeMode` annotation documentation section>> for examples and further details. |
|
|
|
|
|
[[testcontext-parallel-test-execution]] |
|
== Parallel Test Execution |
|
|
|
Spring Framework 5.0 introduced basic support for executing tests in parallel within a |
|
single JVM when using the Spring TestContext Framework. In general, this means that most |
|
test classes or test methods can be run in parallel without any changes to test code |
|
or configuration. |
|
|
|
TIP: For details on how to set up parallel test execution, see the documentation for your |
|
testing framework, build tool, or IDE. |
|
|
|
Keep in mind that the introduction of concurrency into your test suite can result in |
|
unexpected side effects, strange runtime behavior, and tests that fail intermittently or |
|
seemingly randomly. The Spring Team therefore provides the following general guidelines |
|
for when not to run tests in parallel. |
|
|
|
Do not run tests in parallel if the tests: |
|
|
|
* Use Spring Framework's `@DirtiesContext` support. |
|
* Use Spring Boot's `@MockBean` or `@SpyBean` support. |
|
* Use JUnit 4's `@FixMethodOrder` support or any testing framework feature |
|
that is designed to ensure that test methods run in a particular order. Note, |
|
however, that this does not apply if entire test classes are run in parallel. |
|
* Change the state of shared services or systems such as a database, message broker, |
|
filesystem, and others. This applies to both embedded and external systems. |
|
|
|
[TIP] |
|
==== |
|
If parallel test execution fails with an exception stating that the `ApplicationContext` |
|
for the current test is no longer active, this typically means that the |
|
`ApplicationContext` was removed from the `ContextCache` in a different thread. |
|
|
|
This may be due to the use of `@DirtiesContext` or due to automatic eviction from the |
|
`ContextCache`. If `@DirtiesContext` is the culprit, you either need to find a way to |
|
avoid using `@DirtiesContext` or exclude such tests from parallel execution. If the |
|
maximum size of the `ContextCache` has been exceeded, you can increase the maximum size |
|
of the cache. See the discussion on <<testcontext-ctx-management-caching, context caching>> |
|
for details. |
|
==== |
|
|
|
WARNING: Parallel test execution in the Spring TestContext Framework is only possible if |
|
the underlying `TestContext` implementation provides a copy constructor, as explained in |
|
the javadoc for {api-spring-framework}/test/context/TestContext.html[`TestContext`]. The |
|
`DefaultTestContext` used in Spring provides such a constructor. However, if you use a |
|
third-party library that provides a custom `TestContext` implementation, you need to |
|
verify that it is suitable for parallel test execution. |
|
|
|
|
|
[[testcontext-support-classes]] |
|
== TestContext Framework Support Classes |
|
|
|
This section describes the various classes that support the Spring TestContext Framework. |
|
|
|
[[testcontext-junit4-runner]] |
|
=== Spring JUnit 4 Runner |
|
|
|
The Spring TestContext Framework offers full integration with JUnit 4 through a custom |
|
runner (supported on JUnit 4.12 or higher). By annotating test classes with |
|
`@RunWith(SpringJUnit4ClassRunner.class)` or the shorter `@RunWith(SpringRunner.class)` |
|
variant, developers can implement standard JUnit 4-based unit and integration tests and |
|
simultaneously reap the benefits of the TestContext framework, such as support for |
|
loading application contexts, dependency injection of test instances, transactional test |
|
method execution, and so on. If you want to use the Spring TestContext Framework with an |
|
alternative runner (such as JUnit 4's `Parameterized` runner) or third-party runners |
|
(such as the `MockitoJUnitRunner`), you can, optionally, use |
|
<<testcontext-junit4-rules, Spring's support for JUnit rules>> instead. |
|
|
|
The following code listing shows the minimal requirements for configuring a test class to |
|
run with the custom Spring `Runner`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RunWith(SpringRunner.class) |
|
@TestExecutionListeners({}) |
|
public class SimpleTest { |
|
|
|
@Test |
|
public void testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RunWith(SpringRunner::class) |
|
@TestExecutionListeners |
|
class SimpleTest { |
|
|
|
@Test |
|
fun testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
In the preceding example, `@TestExecutionListeners` is configured with an empty list, to |
|
disable the default listeners, which otherwise would require an `ApplicationContext` to |
|
be configured through `@ContextConfiguration`. |
|
|
|
[[testcontext-junit4-rules]] |
|
=== Spring JUnit 4 Rules |
|
|
|
The `org.springframework.test.context.junit4.rules` package provides the following JUnit |
|
4 rules (supported on JUnit 4.12 or higher): |
|
|
|
* `SpringClassRule` |
|
* `SpringMethodRule` |
|
|
|
`SpringClassRule` is a JUnit `TestRule` that supports class-level features of the Spring |
|
TestContext Framework, whereas `SpringMethodRule` is a JUnit `MethodRule` that supports |
|
instance-level and method-level features of the Spring TestContext Framework. |
|
|
|
In contrast to the `SpringRunner`, Spring's rule-based JUnit support has the advantage of |
|
being independent of any `org.junit.runner.Runner` implementation and can, therefore, be |
|
combined with existing alternative runners (such as JUnit 4's `Parameterized`) or |
|
third-party runners (such as the `MockitoJUnitRunner`). |
|
|
|
To support the full functionality of the TestContext framework, you must combine a |
|
`SpringClassRule` with a `SpringMethodRule`. The following example shows the proper way |
|
to declare these rules in an integration test: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Optionally specify a non-Spring Runner via @RunWith(...) |
|
@ContextConfiguration |
|
public class IntegrationTest { |
|
|
|
@ClassRule |
|
public static final SpringClassRule springClassRule = new SpringClassRule(); |
|
|
|
@Rule |
|
public final SpringMethodRule springMethodRule = new SpringMethodRule(); |
|
|
|
@Test |
|
public void testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Optionally specify a non-Spring Runner via @RunWith(...) |
|
@ContextConfiguration |
|
class IntegrationTest { |
|
|
|
@Rule |
|
val springMethodRule = SpringMethodRule() |
|
|
|
@Test |
|
fun testMethod() { |
|
// test logic... |
|
} |
|
|
|
companion object { |
|
@ClassRule |
|
val springClassRule = SpringClassRule() |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-support-classes-junit4]] |
|
=== JUnit 4 Support Classes |
|
|
|
The `org.springframework.test.context.junit4` package provides the following support |
|
classes for JUnit 4-based test cases (supported on JUnit 4.12 or higher): |
|
|
|
* `AbstractJUnit4SpringContextTests` |
|
* `AbstractTransactionalJUnit4SpringContextTests` |
|
|
|
`AbstractJUnit4SpringContextTests` is an abstract base test class that integrates the |
|
Spring TestContext Framework with explicit `ApplicationContext` testing support in a |
|
JUnit 4 environment. When you extend `AbstractJUnit4SpringContextTests`, you can access a |
|
`protected` `applicationContext` instance variable that you can use to perform explicit |
|
bean lookups or to test the state of the context as a whole. |
|
|
|
`AbstractTransactionalJUnit4SpringContextTests` is an abstract transactional extension of |
|
`AbstractJUnit4SpringContextTests` that adds some convenience functionality for JDBC |
|
access. This class expects a `javax.sql.DataSource` bean and a |
|
`PlatformTransactionManager` bean to be defined in the `ApplicationContext`. When you |
|
extend `AbstractTransactionalJUnit4SpringContextTests`, you can access a `protected` |
|
`jdbcTemplate` instance variable that you can use to run SQL statements to query the |
|
database. You can use such queries to confirm database state both before and after |
|
running database-related application code, and Spring ensures that such queries run in |
|
the scope of the same transaction as the application code. When used in conjunction with |
|
an ORM tool, be sure to avoid <<testcontext-tx-false-positives, false positives>>. |
|
As mentioned in <<integration-testing-support-jdbc>>, |
|
`AbstractTransactionalJUnit4SpringContextTests` also provides convenience methods that |
|
delegate to methods in `JdbcTestUtils` by using the aforementioned `jdbcTemplate`. |
|
Furthermore, `AbstractTransactionalJUnit4SpringContextTests` provides an |
|
`executeSqlScript(..)` method for running SQL scripts against the configured `DataSource`. |
|
|
|
TIP: These classes are a convenience for extension. If you do not want your test classes |
|
to be tied to a Spring-specific class hierarchy, you can configure your own custom test |
|
classes by using `@RunWith(SpringRunner.class)` or <<testcontext-junit4-rules, Spring's |
|
JUnit rules>>. |
|
|
|
[[testcontext-junit-jupiter-extension]] |
|
=== SpringExtension for JUnit Jupiter |
|
|
|
The Spring TestContext Framework offers full integration with the JUnit Jupiter testing |
|
framework, introduced in JUnit 5. By annotating test classes with |
|
`@ExtendWith(SpringExtension.class)`, you can implement standard JUnit Jupiter-based unit |
|
and integration tests and simultaneously reap the benefits of the TestContext framework, |
|
such as support for loading application contexts, dependency injection of test instances, |
|
transactional test method execution, and so on. |
|
|
|
Furthermore, thanks to the rich extension API in JUnit Jupiter, Spring provides the |
|
following features above and beyond the feature set that Spring supports for JUnit 4 and |
|
TestNG: |
|
|
|
* Dependency injection for test constructors, test methods, and test lifecycle callback |
|
methods. See <<testcontext-junit-jupiter-di>> for further details. |
|
* Powerful support for link:https://junit.org/junit5/docs/current/user-guide/#extensions-conditions[conditional |
|
test execution] based on SpEL expressions, environment variables, system properties, |
|
and so on. See the documentation for `@EnabledIf` and `@DisabledIf` in |
|
<<integration-testing-annotations-junit-jupiter>> for further details and examples. |
|
* Custom composed annotations that combine annotations from Spring and JUnit Jupiter. See |
|
the `@TransactionalDevTestConfig` and `@TransactionalIntegrationTest` examples in |
|
<<integration-testing-annotations-meta>> for further details. |
|
|
|
The following code listing shows how to configure a test class to use the |
|
`SpringExtension` in conjunction with `@ContextConfiguration`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Instructs JUnit Jupiter to extend the test with Spring support. |
|
@ExtendWith(SpringExtension.class) |
|
// Instructs Spring to load an ApplicationContext from TestConfig.class |
|
@ContextConfiguration(classes = TestConfig.class) |
|
class SimpleTests { |
|
|
|
@Test |
|
void testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Instructs JUnit Jupiter to extend the test with Spring support. |
|
@ExtendWith(SpringExtension::class) |
|
// Instructs Spring to load an ApplicationContext from TestConfig::class |
|
@ContextConfiguration(classes = [TestConfig::class]) |
|
class SimpleTests { |
|
|
|
@Test |
|
fun testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
Since you can also use annotations in JUnit 5 as meta-annotations, Spring provides the |
|
`@SpringJUnitConfig` and `@SpringJUnitWebConfig` composed annotations to simplify the |
|
configuration of the test `ApplicationContext` and JUnit Jupiter. |
|
|
|
The following example uses `@SpringJUnitConfig` to reduce the amount of configuration |
|
used in the previous example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Instructs Spring to register the SpringExtension with JUnit |
|
// Jupiter and load an ApplicationContext from TestConfig.class |
|
@SpringJUnitConfig(TestConfig.class) |
|
class SimpleTests { |
|
|
|
@Test |
|
void testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Instructs Spring to register the SpringExtension with JUnit |
|
// Jupiter and load an ApplicationContext from TestConfig.class |
|
@SpringJUnitConfig(TestConfig::class) |
|
class SimpleTests { |
|
|
|
@Test |
|
fun testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
Similarly, the following example uses `@SpringJUnitWebConfig` to create a |
|
`WebApplicationContext` for use with JUnit Jupiter: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Instructs Spring to register the SpringExtension with JUnit |
|
// Jupiter and load a WebApplicationContext from TestWebConfig.class |
|
@SpringJUnitWebConfig(TestWebConfig.class) |
|
class SimpleWebTests { |
|
|
|
@Test |
|
void testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Instructs Spring to register the SpringExtension with JUnit |
|
// Jupiter and load a WebApplicationContext from TestWebConfig::class |
|
@SpringJUnitWebConfig(TestWebConfig::class) |
|
class SimpleWebTests { |
|
|
|
@Test |
|
fun testMethod() { |
|
// test logic... |
|
} |
|
} |
|
---- |
|
|
|
See the documentation for `@SpringJUnitConfig` and `@SpringJUnitWebConfig` in |
|
<<integration-testing-annotations-junit-jupiter>> for further details. |
|
|
|
[[testcontext-junit-jupiter-di]] |
|
==== Dependency Injection with `SpringExtension` |
|
|
|
`SpringExtension` implements the |
|
link:https://junit.org/junit5/docs/current/user-guide/#extensions-parameter-resolution[`ParameterResolver`] |
|
extension API from JUnit Jupiter, which lets Spring provide dependency injection for test |
|
constructors, test methods, and test lifecycle callback methods. |
|
|
|
Specifically, `SpringExtension` can inject dependencies from the test's |
|
`ApplicationContext` into test constructors and methods that are annotated with |
|
`@BeforeAll`, `@AfterAll`, `@BeforeEach`, `@AfterEach`, `@Test`, `@RepeatedTest`, |
|
`@ParameterizedTest`, and others. |
|
|
|
[[testcontext-junit-jupiter-di-constructor]] |
|
===== Constructor Injection |
|
|
|
If a specific parameter in a constructor for a JUnit Jupiter test class is of type |
|
`ApplicationContext` (or a sub-type thereof) or is annotated or meta-annotated with |
|
`@Autowired`, `@Qualifier`, or `@Value`, Spring injects the value for that specific |
|
parameter with the corresponding bean or value from the test's `ApplicationContext`. |
|
|
|
Spring can also be configured to autowire all arguments for a test class constructor if |
|
the constructor is considered to be _autowirable_. A constructor is considered to be |
|
autowirable if one of the following conditions is met (in order of precedence). |
|
|
|
* The constructor is annotated with `@Autowired`. |
|
* `@TestConstructor` is present or meta-present on the test class with the `autowireMode` |
|
attribute set to `ALL`. |
|
* The default _test constructor autowire mode_ has been changed to `ALL`. |
|
|
|
See <<integration-testing-annotations-testconstructor>> for details on the use of |
|
`@TestConstructor` and how to change the global _test constructor autowire mode_. |
|
|
|
WARNING: If the constructor for a test class is considered to be _autowirable_, Spring |
|
assumes the responsibility for resolving arguments for all parameters in the constructor. |
|
Consequently, no other `ParameterResolver` registered with JUnit Jupiter can resolve |
|
parameters for such a constructor. |
|
|
|
[WARNING] |
|
==== |
|
Constructor injection for test classes must not be used in conjunction with JUnit |
|
Jupiter's `@TestInstance(PER_CLASS)` support if `@DirtiesContext` is used to close the |
|
test's `ApplicationContext` before or after test methods. |
|
|
|
The reason is that `@TestInstance(PER_CLASS)` instructs JUnit Jupiter to cache the test |
|
instance between test method invocations. Consequently, the test instance will retain |
|
references to beans that were originally injected from an `ApplicationContext` that has |
|
been subsequently closed. Since the constructor for the test class will only be invoked |
|
once in such scenarios, dependency injection will not occur again, and subsequent tests |
|
will interact with beans from the closed `ApplicationContext` which may result in errors. |
|
|
|
To use `@DirtiesContext` with "before test method" or "after test method" modes in |
|
conjunction with `@TestInstance(PER_CLASS)`, one must configure dependencies from Spring |
|
to be supplied via field or setter injection so that they can be re-injected between test |
|
method invocations. |
|
==== |
|
|
|
In the following example, Spring injects the `OrderService` bean from the |
|
`ApplicationContext` loaded from `TestConfig.class` into the |
|
`OrderServiceIntegrationTests` constructor. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestConfig.class) |
|
class OrderServiceIntegrationTests { |
|
|
|
private final OrderService orderService; |
|
|
|
@Autowired |
|
OrderServiceIntegrationTests(OrderService orderService) { |
|
this.orderService = orderService; |
|
} |
|
|
|
// tests that use the injected OrderService |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestConfig::class) |
|
class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){ |
|
// tests that use the injected OrderService |
|
} |
|
|
|
---- |
|
|
|
Note that this feature lets test dependencies be `final` and therefore immutable. |
|
|
|
If the `spring.test.constructor.autowire.mode` property is to `all` (see |
|
<<integration-testing-annotations-testconstructor>>), we can omit the declaration of |
|
`@Autowired` on the constructor in the previous example, resulting in the following. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestConfig.class) |
|
class OrderServiceIntegrationTests { |
|
|
|
private final OrderService orderService; |
|
|
|
OrderServiceIntegrationTests(OrderService orderService) { |
|
this.orderService = orderService; |
|
} |
|
|
|
// tests that use the injected OrderService |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestConfig::class) |
|
class OrderServiceIntegrationTests(val orderService:OrderService) { |
|
// tests that use the injected OrderService |
|
} |
|
---- |
|
|
|
[[testcontext-junit-jupiter-di-method]] |
|
===== Method Injection |
|
|
|
If a parameter in a JUnit Jupiter test method or test lifecycle callback method is of |
|
type `ApplicationContext` (or a sub-type thereof) or is annotated or meta-annotated with |
|
`@Autowired`, `@Qualifier`, or `@Value`, Spring injects the value for that specific |
|
parameter with the corresponding bean from the test's `ApplicationContext`. |
|
|
|
In the following example, Spring injects the `OrderService` from the `ApplicationContext` |
|
loaded from `TestConfig.class` into the `deleteOrder()` test method: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestConfig.class) |
|
class OrderServiceIntegrationTests { |
|
|
|
@Test |
|
void deleteOrder(@Autowired OrderService orderService) { |
|
// use orderService from the test's ApplicationContext |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestConfig::class) |
|
class OrderServiceIntegrationTests { |
|
|
|
@Test |
|
fun deleteOrder(@Autowired orderService: OrderService) { |
|
// use orderService from the test's ApplicationContext |
|
} |
|
} |
|
---- |
|
|
|
Due to the robustness of the `ParameterResolver` support in JUnit Jupiter, you can also |
|
have multiple dependencies injected into a single method, not only from Spring but also |
|
from JUnit Jupiter itself or other third-party extensions. |
|
|
|
The following example shows how to have both Spring and JUnit Jupiter inject dependencies |
|
into the `placeOrderRepeatedly()` test method simultaneously. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestConfig.class) |
|
class OrderServiceIntegrationTests { |
|
|
|
@RepeatedTest(10) |
|
void placeOrderRepeatedly(RepetitionInfo repetitionInfo, |
|
@Autowired OrderService orderService) { |
|
|
|
// use orderService from the test's ApplicationContext |
|
// and repetitionInfo from JUnit Jupiter |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestConfig::class) |
|
class OrderServiceIntegrationTests { |
|
|
|
@RepeatedTest(10) |
|
fun placeOrderRepeatedly(repetitionInfo:RepetitionInfo, @Autowired orderService:OrderService) { |
|
|
|
// use orderService from the test's ApplicationContext |
|
// and repetitionInfo from JUnit Jupiter |
|
} |
|
} |
|
---- |
|
|
|
Note that the use of `@RepeatedTest` from JUnit Jupiter lets the test method gain access |
|
to the `RepetitionInfo`. |
|
|
|
[[testcontext-junit-jupiter-nested-test-configuration]] |
|
==== `@Nested` test class configuration |
|
|
|
The _Spring TestContext Framework_ has supported the use of test-related annotations on |
|
`@Nested` test classes in JUnit Jupiter since Spring Framework 5.0; however, until Spring |
|
Framework 5.3 class-level test configuration annotations were not _inherited_ from |
|
enclosing classes like they are from superclasses. |
|
|
|
Spring Framework 5.3 introduces first-class support for inheriting test class |
|
configuration from enclosing classes, and such configuration will be inherited by |
|
default. To change from the default `INHERIT` mode to `OVERRIDE` mode, you may annotate |
|
an individual `@Nested` test class with |
|
`@NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)`. An explicit |
|
`@NestedTestConfiguration` declaration will apply to the annotated test class as well as |
|
any of its subclasses and nested classes. Thus, you may annotate a top-level test class |
|
with `@NestedTestConfiguration`, and that will apply to all of its nested test classes |
|
recursively. |
|
|
|
In order to allow development teams to change the default to `OVERRIDE` – for example, |
|
for compatibility with Spring Framework 5.0 through 5.2 – the default mode can be changed |
|
globally via a JVM system property or a `spring.properties` file in the root of the |
|
classpath. See the <<integration-testing-annotations-nestedtestconfiguration, "Changing |
|
the default enclosing configuration inheritance mode">> note for details. |
|
|
|
Although the following "Hello World" example is very simplistic, it shows how to declare |
|
common configuration on a top-level class that is inherited by its `@Nested` test |
|
classes. In this particular example, only the `TestConfig` configuration class is |
|
inherited. Each nested test class provides its own set of active profiles, resulting in a |
|
distinct `ApplicationContext` for each nested test class (see |
|
<<testcontext-ctx-management-caching>> for details). Consult the list of |
|
<<integration-testing-annotations-nestedtestconfiguration, supported annotations>> to see |
|
which annotations can be inherited in `@Nested` test classes. |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@SpringJUnitConfig(TestConfig.class) |
|
class GreetingServiceTests { |
|
|
|
@Nested |
|
@ActiveProfiles("lang_en") |
|
class EnglishGreetings { |
|
|
|
@Test |
|
void hello(@Autowired GreetingService service) { |
|
assertThat(service.greetWorld()).isEqualTo("Hello World"); |
|
} |
|
} |
|
|
|
@Nested |
|
@ActiveProfiles("lang_de") |
|
class GermanGreetings { |
|
|
|
@Test |
|
void hello(@Autowired GreetingService service) { |
|
assertThat(service.greetWorld()).isEqualTo("Hallo Welt"); |
|
} |
|
} |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@SpringJUnitConfig(TestConfig::class) |
|
class GreetingServiceTests { |
|
|
|
@Nested |
|
@ActiveProfiles("lang_en") |
|
inner class EnglishGreetings { |
|
|
|
@Test |
|
fun hello(@Autowired service:GreetingService) { |
|
assertThat(service.greetWorld()).isEqualTo("Hello World") |
|
} |
|
} |
|
|
|
@Nested |
|
@ActiveProfiles("lang_de") |
|
inner class GermanGreetings { |
|
|
|
@Test |
|
fun hello(@Autowired service:GreetingService) { |
|
assertThat(service.greetWorld()).isEqualTo("Hallo Welt") |
|
} |
|
} |
|
} |
|
---- |
|
|
|
[[testcontext-support-classes-testng]] |
|
=== TestNG Support Classes |
|
|
|
The `org.springframework.test.context.testng` package provides the following support |
|
classes for TestNG based test cases: |
|
|
|
* `AbstractTestNGSpringContextTests` |
|
* `AbstractTransactionalTestNGSpringContextTests` |
|
|
|
`AbstractTestNGSpringContextTests` is an abstract base test class that integrates the |
|
Spring TestContext Framework with explicit `ApplicationContext` testing support in a |
|
TestNG environment. When you extend `AbstractTestNGSpringContextTests`, you can access a |
|
`protected` `applicationContext` instance variable that you can use to perform explicit |
|
bean lookups or to test the state of the context as a whole. |
|
|
|
`AbstractTransactionalTestNGSpringContextTests` is an abstract transactional extension of |
|
`AbstractTestNGSpringContextTests` that adds some convenience functionality for JDBC |
|
access. This class expects a `javax.sql.DataSource` bean and a |
|
`PlatformTransactionManager` bean to be defined in the `ApplicationContext`. When you |
|
extend `AbstractTransactionalTestNGSpringContextTests`, you can access a `protected` |
|
`jdbcTemplate` instance variable that you can use to run SQL statements to query the |
|
database. You can use such queries to confirm database state both before and after |
|
running database-related application code, and Spring ensures that such queries run in |
|
the scope of the same transaction as the application code. When used in conjunction with |
|
an ORM tool, be sure to avoid <<testcontext-tx-false-positives, false positives>>. |
|
As mentioned in <<integration-testing-support-jdbc>>, |
|
`AbstractTransactionalTestNGSpringContextTests` also provides convenience methods that |
|
delegate to methods in `JdbcTestUtils` by using the aforementioned `jdbcTemplate`. |
|
Furthermore, `AbstractTransactionalTestNGSpringContextTests` provides an |
|
`executeSqlScript(..)` method for running SQL scripts against the configured `DataSource`. |
|
|
|
TIP: These classes are a convenience for extension. If you do not want your test classes |
|
to be tied to a Spring-specific class hierarchy, you can configure your own custom test |
|
classes by using `@ContextConfiguration`, `@TestExecutionListeners`, and so on and by |
|
manually instrumenting your test class with a `TestContextManager`. See the source code |
|
of `AbstractTestNGSpringContextTests` for an example of how to instrument your test class. |
|
|
|
[[testcontext-aot]] |
|
== Ahead of Time Support for Tests |
|
|
|
This chapter covers Spring's Ahead of Time (AOT) support for integration tests using the |
|
Spring TestContext Framework. |
|
|
|
The testing support extends Spring's <<core.adoc#core.aot,core AOT support>> with the |
|
following features. |
|
|
|
* Build-time detection of all integration tests in the current project that use the |
|
TestContext framework to load an `ApplicationContext`. |
|
- Provides explicit support for test classes based on JUnit Jupiter and JUnit 4 as well |
|
as implicit support for TestNG and other testing frameworks that use Spring's core |
|
testing annotations -- as long as the tests are run using a JUnit Platform |
|
`TestEngine` that is registered for the current project. |
|
* Build-time AOT processing: each unique test `ApplicationContext` in the current project |
|
will be <<core.adoc#core.aot.refresh,refreshed for AOT processing>>. |
|
* Runtime AOT support: when executing in AOT runtime mode, a Spring integration test will |
|
use an AOT-optimized `ApplicationContext` that participates transparently with the |
|
<<testcontext-ctx-management-caching, context cache>>. |
|
|
|
[WARNING] |
|
==== |
|
The `@ContextHierarchy` annotation is currently not supported in AOT mode. |
|
==== |
|
|
|
To provide test-specific runtime hints for use within a GraalVM native image, you have |
|
the following options. |
|
|
|
* Implement a custom |
|
{api-spring-framework}/test/context/aot/TestRuntimeHintsRegistrar.html[`TestRuntimeHintsRegistrar`] |
|
and register it globally via `META-INF/spring/aot.factories`. |
|
* Implement a custom {api-spring-framework}/aot/hint/RuntimeHintsRegistrar.html[`RuntimeHintsRegistrar`] |
|
and register it globally via `META-INF/spring/aot.factories` or locally on a test class |
|
via {api-spring-framework}/context/annotation/ImportRuntimeHints.html[`@ImportRuntimeHints`]. |
|
* Annotate a test class with {api-spring-framework}/aot/hint/annotation/Reflective.html[`@Reflective`] or |
|
{api-spring-framework}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`]. |
|
* See <<core.adoc#core.aot.hints,Runtime Hints>> for details on Spring's core runtime hints |
|
and annotation support. |
|
|
|
[TIP] |
|
==== |
|
The `TestRuntimeHintsRegistrar` API serves as a companion to the core |
|
`RuntimeHintsRegistrar` API. If you need to register global hints for testing support |
|
that are not specific to particular test classes, favor implementing |
|
`RuntimeHintsRegistrar` over the test-specific API. |
|
==== |
|
|
|
If you implement a custom `ContextLoader`, it must implement |
|
{api-spring-framework}/test/context/aot/AotContextLoader.html[`AotContextLoader`] in |
|
order to provide AOT build-time processing and AOT runtime execution support. Note, |
|
however, that all context loader implementations provided by the Spring Framework and |
|
Spring Boot already implement `AotContextLoader`. |
|
|
|
If you implement a custom `TestExecutionListener`, it must implement |
|
{api-spring-framework}/test/context/aot/AotTestExecutionListener.html[`AotTestExecutionListener`] |
|
in order to participate in AOT processing. See the `SqlScriptsTestExecutionListener` in |
|
the `spring-test` module for an example.
|
|
|