This commit introduces TestContextRuntimeHints which is a
RuntimeHintsRegistrar implementation that makes core types and
annotations from the Spring TestContext Framework available at runtime
within a GraalVM native image.
TestContextRuntimeHints is registered automatically via the
"META-INF/spring/aot.factories" file in spring-test.
This commit also modifies TestContextAotGeneratorTests to assert the
expected runtime hints registered by TestContextRuntimeHints as well as
runtime hints for TestExecutionListener and ContextCustomizerFactory
implementations registered by SpringFactoriesLoaderRuntimeHints.
Closes gh-29028
Closes gh-29044
This commit introduces a new feature in the Spring TestContext
Framework (TCF) that provides support for recording 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 one to process the events as a java.util.Stream.
The following example demonstrates usage of this new feature.
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {
@Autowired
OrderService orderService;
@Autowired
ApplicationEvents events;
@Test
void submitOrder() {
// Invoke method in OrderService that publishes an event
orderService.submitOrder(new Order(/* ... */));
// Verify that an OrderSubmitted event was published
int numEvents = events.stream(OrderSubmitted.class).count();
assertThat(numEvents).isEqualTo(1);
}
}
To enable the feature, a test class must be annotated or meta-annotated
with @RecordApplicationEvents. Behind the scenes, a new
ApplicationEventsTestExecutionListener manages the registration of
ApplicationEvents for the current thread at various points within the
test execution lifecycle and makes the current instance of
ApplicationEvents available to tests via an @Autowired field in the
test class. The latter is made possible by a custom ObjectFactory that
is registered as a "resolvable dependency". Thanks to the magic of
ObjectFactoryDelegatingInvocationHandler in the spring-beans module,
the ApplicationEvents instance injected into test classes is
effectively a scoped-proxy that always accesses the ApplicationEvents
managed for the current test.
The current ApplicationEvents instance is stored in a ThreadLocal
variable which is made available in the ApplicationEventsHolder.
Although this class is public, it is only intended for use within the
TCF or in the implementation of third-party extensions.
ApplicationEventsApplicationListener is responsible for listening to
all application events and recording them in the current
ApplicationEvents instance. A single
ApplicationEventsApplicationListener is registered with the test's
ApplicationContext by the ApplicationEventsTestExecutionListener.
The SpringExtension has also been updated to support parameters of type
ApplicationEvents via the JUnit Jupiter ParameterResolver extension
API. This allows JUnit Jupiter based tests to receive access to the
current ApplicationEvents via test and lifecycle method parameters as
an alternative to @Autowired fields in the test class.
Closes gh-25616
This commit introduces a @DynamicPropertySource annotation that can be
used on methods in test classes that want to add properties to the
Environment with a dynamically supplied value.
This new feature can be used in conjunction with Testcontainers and
other frameworks that manage resources outside the lifecycle of a
test's ApplicationContext.
Closes gh-24540
Co-authored-by: Phillip Webb <pwebb@pivotal.io>
This commit registers the EventPublishingTestExecutionListener as a
default TestExecutionListener with an order of 10,000. This registers
the EventPublishingTestExecutionListener as the last listener provided
by the Spring Framework.
With EventPublishingTestExecutionListener registered with an order of
10,000, it is effectively wrapped by all other Spring listeners,
including support for @DirtiesContext and test-managed transactions.
Furthermore, this commit revises the implementation of
EventPublishingTestExecutionListener to take advantage of the new
TestContext#hasApplicationContext() support which allows the
EventPublishingTestExecutionListener to publish events only if the
test's ApplicationContext is currently available. This avoids
undesirable side-effects such as eager loading of the
ApplicationContext before it is needed or re-loading of the
ApplicationContext after it has been intentionally closed.
Closes gh-18490
Prior to this commit, any attempt to include a bean of type
ServletServerContainerFactoryBean in the WebApplicationContext for an
integration test class annotated with @WebAppConfiguration in
conjunction the Spring TestContext Framework (TCF) would have resulted
in an IllegalStateException stating that "A ServletContext is required
to access the javax.websocket.server.ServerContainer instance."
In such scenarios, the MockServletContext was in fact present in the
WebApplicationContext; however there was no WebSocket ServerContainer
stored in the ServletContext.
This commit addresses this issue by introducing the following.
- MockServerContainer: a private mock implementation of the
javax.websocket.server.ServerContainer interface.
- MockServerContainerContextCustomizer: a ContextCustomizer that
instantiates a new MockServerContainer and stores it in the
ServletContext under the attribute named
"javax.websocket.server.ServerContainer".
- MockServerContainerContextCustomizerFactory: a
ContextCustomizerFactory which creates a
MockServerContainerContextCustomizer if WebSocket support is present
in the classpath and the test class is annotated with
@WebAppConfiguration. This factory is registered by default via the
spring.factories mechanism.
Issue: SPR-14367
SPR-12429 introduced various `BEFORE_*` modes in `@DirtiesContext`. To
support these new modes, `DirtiesContextTestExecutionListener` (DCTEL)
was updated to support both `BEFORE_*` and `AFTER_*` modes. However,
there is a problem with having DCTEL support `BEFORE_*` modes since it
is typically configured to execute after the
`DependencyInjectionTestExecutionListener` (DITEL), and this leads to
several undesired side effects:
- The test's `ApplicationContext` is closed by DCTEL *after*
dependencies have been injected into the test instance.
- Injected dependencies may therefore attempt to interact with an
`ApplicationContext` that is no longer _active_.
- If a test has its `ApplicationContext` injected as a dependency,
interaction with the context will likely fail since the context has
been closed.
- Any `TestExecutionListeners` registered after DCTEL will get a _new_
`ApplicationContext` if they invoke `getApplicationContext()` on the
`TestContext`.
This commit fixes these issues by introducing a new
`DirtiesContextBeforeModesTestExecutionListener` (DCBMTEL) that is
registered by default before DITEL. The previous support for `BEFORE_*`
modes has been moved from DCTEL to DCBMTEL. In addition, an
`AbstractDirtiesContextTestExecutionListener` has been extracted from
DCTEL in order to avoid code duplication.
Issue: SPR-13180
Prior to this commit, there was no declarative mechanism for a custom
TestExecutionListener to be registered as a default
TestExecutionListener.
This commit introduces support for discovering default
TestExecutionListener implementations via the SpringFactoriesLoader
mechanism. Specifically, the spring-test module declares all core
default TestExecutionListeners under the
org.springframework.test.context.TestExecutionListener key in its
META-INF/spring.factories properties file, and third-party frameworks
and developers can contribute to the list of default
TestExecutionListeners in the same manner.
- AbstractTestContextBootstrapper uses the SpringFactoriesLoader to
look up the class names of all registered default
TestExecutionListeners and sorts the instantiated listeners using
AnnotationAwareOrderComparator.
- DefaultTestContextBootstrapper and WebTestContextBootstrapper now
rely on the SpringFactoriesLoader mechanism for finding default
TestExecutionListeners instead of hard coding fully qualified class
names.
- To ensure that default TestExecutionListeners are registered in the
correct order, each can implement Ordered or declare @Order.
- AbstractTestExecutionListener and all default TestExecutionListeners
provided by Spring now implement Ordered with appropriate values.
- Introduced "copy constructors" in MergedContextConfiguration and
WebMergedContextConfiguration
- SpringFactoriesLoader now uses AnnotationAwareOrderComparator
instead of OrderComparator.
Issue: SPR-11466