Starting with Spring Framework 5.3.3, the TestContext framework provides support for recording application events
published in the ApplicationContext
so that tests can be asserted against those events. All events
published during the execution of a single test are available through the ApplicationEvents
API, which
allows events to be processed as java.util.Stream
.
To use ApplicationEvents
in your tests, do the following.
-
Make sure your test class is annotated or meta-annotated with
@RecordApplicationEvents
. -
Make sure the
ApplicationEventsTestExecutionListener
is registered. Note thatApplicationEventsTestExecutionListener
is registered by default, and you only need to register it manually if you have a special configuration via the@TestExecutionListener
annotation that does not include listeners by default . -
Annotate a field of type
ApplicationEvents
with the@Autowired
annotation and use this instance ofApplicationEvents
in your test methods and lifecycle methods (e.g. methods , marked with@BeforeEach
and@AfterEach
annotations in JUnit Jupiter).-
When using the SpringExtension for JUnit Jupiter, you can declare a method parameter of type
ApplicationEvents
in a test or lifecycle method as an alternative to a@Autowired
annotated field in the test class.
-
The following test class uses SpringExtension
for JUnit Jupiter and AssertJ for assertion types of application events
published when a method is called in a Spring managed bean:
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {
@Autowired
OrderService orderService;
@Autowired
ApplicationEvents events;
@Test
void submitOrder() {
// Call a method in OrderService that publishes an event
orderService.submitOrder(new Order(/* ... */));
// Check that the OrderSubmitted event has been published
long numEvents = events.stream(OrderSubmitted.class).count();
assertThat(numEvents).isEqualTo(1);
}
}
- Annotate the test class with
@RecordApplicationEvents
. - Inject an instance of
ApplicationEvents
for the current test. - Use the
ApplicationEvents
API to count how manyOrderSubmitted
events have been published.
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {
@Autowired
lateinit var orderService: OrderService
@Autowired
lateinit var events: ApplicationEvents
@Test
fun submitOrder() {
// Call a method in OrderService that publishes the event
orderService.submitOrder(Order(/* ... */))
// Check that the OrderSubmitted event has been published
val numEvents = events.stream(OrderSubmitted::class).count()
assertThat(numEvents).isEqualTo(1)
}
}
- Annotate the test class with
@RecordApplicationEvents
. - Inject an instance of
ApplicationEvents
for the current test. - Use the
ApplicationEvents
API to count how manyOrderSubmitted
events have been published.
See javadoc by ApplicationEvents
for more information about the
ApplicationEvents
API.
Test execution events
The EventPublishingTestExecutionListener
listener, introduced in Spring Framework 5.2, offers an
alternative approach to implementing a custom TestExecutionListener
. Components in the ApplicationContext
test 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 can be consumed for a variety of reasons, such as resetting mock beans or tracking test execution. One
advantage of consuming test execution events instead of implementing a special TestExecutionListener
is
that test execution events can be consumed by any Spring bean registered in the test ApplicationContext
,
and such beans can directly benefit dependency injection and other ApplicationContext
functions. In
contrast, TestExecutionListener
is not a bean in ApplicationContext
.
The EventPublishingTestExecutionListener
listener is registered by default; however, it only
publishes events if the ApplicationContext
is already loaded. This prevents the ApplicationContext
from being loaded unnecessarily or too early.
Hence, the BeforeTestClassEvent
event will only be published after the
ApplicationContext
has been loaded by another TestExecutionListener
. For example, with
a set of TestExecutionListener
implementations registered by default, the BeforeTestClassEvent
event will not be published for the first test class that uses a particular test ApplicationContext
,
but the event The 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 be
loaded when subsequent ones are executed test classes (unless the context has been removed from the ContextCache
via the @DirtiesContext
annotation or the eviction policy when the maximum size is reached).
If you want a BeforeTestClassEvent
event to always be published for each test class, then you need
to register a TestExecutionListener
that loads the ApplicationContext
in the before
TestClass
callback, and this TestExecutionListener
must be registered before
EventPublishingTestExecutionListener
.
Similarly, if the @DirtiesContext
annotation is used to remove the ApplicationContext
from the context cache after the last test method in a given test class, the AfterTestClassEvent
event will not occur published for this test class.
In order to listen to test execution events, a Spring bean can choose to implement the org.springframework.context.ApplicationListener
interface. Additionally, listener methods can be annotated with @EventListener
and configured to listen
to one of the specific event types listed above (see Listeners
(receivers) of events based on annotations) .
Due to the popularity of this approach, Spring provides the following special @EventListener
annotations to make it easier to register test execution event listeners. These annotations are in the org.springframework.test.context.event.annotation
package.
-
@BeforeTestClass
-
@PrepareTestInstance
-
@BeforeTestMethod
-
@BeforeTestExecution
-
@AfterTestExecution
-
@AfterTestMethod
-
@AfterTestClass
Exception handling
By default, if a test execution event listener throws an exception when consuming an event, that exception is thrown
to the underlying test framework being used (such as JUnit or TestNG). For example, if consuming a BeforeTestMethodEvent
event results in an exception being thrown, then the corresponding test method will fail as a result of that
exception. Conversely, if an asynchronous test execution event listener throws an exception, it is not propagated to
the underlying test framework. For more information about asynchronous exception handling, refer to the javadoc on
@EventListener
at the class level.
Asynchronous listeners
If you want a specific test execution event listener to process events asynchronously, you can use regular annotation support
@Async
from Spring.
For more details, refer to the javadoc on @EventListener
at
the class level.
GO TO FULL VERSION