CodeGym /Java Course /Module 5. Spring /Application Events

Application Events

Module 5. Spring
Level 11 , Lesson 5
Available

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 that ApplicationEventsTestExecutionListener 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 of ApplicationEvents 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:

Java
@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);
    }
}
  1. Annotate the test class with @RecordApplicationEvents.
  2. Inject an instance of ApplicationEvents for the current test.
  3. Use the ApplicationEvents API to count how many OrderSubmitted events have been published.
Kotlin
@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)
    }
}
  1. Annotate the test class with @RecordApplicationEvents.
  2. Inject an instance of ApplicationEvents for the current test.
  3. Use the ApplicationEvents API to count how many OrderSubmitted 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.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION