- Notifications
You must be signed in to change notification settings - Fork 38.9k
Introduce ApplicationEvents abstraction to capture application events published during a test #25616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce ApplicationEvents abstraction to capture application events published during a test #25616
Conversation
| Thanks for the PR! Tentatively slated for 5.3 RC1 for review and potential inclusion in 5.3 |
…d during a test method. PublishedEventsExtension registers a composite ApplicationListener backed by thread-bound individual listeners to capture all application events published during the execution of a test method. Those would declare a PublishedEvents parameter to the method which provides API to define assertions based on the events published: @ExtendWith(PublishedEventsExtension.class) class SampleTests { @test void someTestMethod(PublishedEvents events) { // Filter events by type and predicate assertThat(events.ofType(MyEventType.class).matching(it -> …)).hasSize(2); } }
5170eca to 3eab764 Compare | The current proposal introduces support for the following abstraction. /** * {@code ApplicationEvents} encapsulates all {@linkplain ApplicationEvent * application events} that were fired during the execution of a single test * method. * * @author Sam Brannen * @author Oliver Drotbohm * @since 5.3.1 * @see ApplicationEventsExtension * @see org.springframework.context.ApplicationEvent * @see org.springframework.context.ApplicationListener */ public interface ApplicationEvents { /** * Stream all application events that were fired during test execution. * @return a stream of all application events */ Stream<ApplicationEvent> stream(); /** * Stream all application events or event payloads of the given type that * were fired during test execution. * @param <T> the event type * @param type the type of events or payloads to stream; never {@code null} * @return a stream of all application events or event payloads of the * specified type */ <T> Stream<T> stream(Class<T> type); }Current work on this issue can be viewed in the following branch that builds on top of this PR: master...sbrannen:issues/gh-25616-application-events-extension Tentatively slated for inclusion in |
This reworks the ApplicationEvents support so that it is based solely on a custom TestExecutionListener instead of a ContextCustomizer and a TestExecutionListener. See spring-projectsgh-25616
This reworks the ApplicationEvents support so that the current ApplicationEvents instance can be @Autowired into the test class for use with testing frameworks other than JUnit Jupiter such as JUnit 4 and TestNG. See spring-projectsgh-25616
Now that the current ApplicationEvents instance can be @Autowired into the test class, we no longer have a concrete need for a dedicated JUnit Jupiter ParameterResolver implementation since the SpringExtension for JUnit Jupiter already supports autowiring of beans from the test's ApplicationContext into test constructors, lifecycle methods, and test methods. See spring-projectsgh-25616
…piter This commit modifies the SpringExtension so that parameters of type ApplicationEvents are considered autowired candidates without the presence of @Autowired on the formal parameter declaration. This is analogous to the existing support for injecting the ApplicationContext without the parameter being annotated with @Autowired. See spring-projectsgh-25616
This commit adds proper support for ApplicationEvents when the test instance is shared -- for example, in TestNG or in JUnit Jupiter with @testinstance(PER_CLASS) semantics. See spring-projectsgh-25616
As a proof of concept, this commit introduces scoped proxy support for an ApplicationEvents bean that is managed by the SimpleThreadScope. Although this approach works in general (in terms of providing an ApplicationEvents bean that can be autowired into test classes and retrieved as a bean by third-party extensions), it has a major drawback. Specifically, an ApplicationEvents instance will be created on-demand for the current thread whenever the ThreadBoundApplicationListener attempts to access the ApplicationEvents, and this will occur even if the user has not annotated the test class with @RecordApplicationEvents. In light of the above, this commit will likely be subsequently reverted but remains in tact in the commit history for possible future consideration. See spring-projectsgh-25616
We currently do not support parameter resolution for ApplicationEvents in a test class constructor. In light of that it is better to throw an exception with an explicit error message rather than leave the user confused by a misleading exception being thrown. See spring-projectsgh-25616
Prior to this commit, if multiple tests were being executed in parallel for a test class with per-test-method test instance lifecycle semantics (e.g., in JUnit 4 and the default in JUnit Jupiter), then a race condition could occur when registering the ApplicationEventsApplicationListener in the ApplicationContext. Consequently, multiple instances of ApplicationEventsApplicationListener would be registered resulting in duplicate tracked events in ApplicationEvents. This commit avoids this race condition via a synchronized block in ApplicationEventsTestExecutionListener's registerListenerAndResolvableDependencyIfNecessary() method. See spring-projectsgh-25616
This commit also ensures that ApplicationEventsTestExecutionListener is registered in AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests. See spring-projectsgh-25616
| Lovely! Thanks for all the extra effort you put into this, @sbrannen! 🙇 |
| You're very welcome! Thanks for the idea, the prototype, and for your patience. :) |
PublishedEventsExtensionregisters a compositeApplicationListenerbacked by thread-bound individual listeners to capture all application events published during the execution of a test method. Those would declare aPublishedEventsparameter to the method which provides API to define assertions based on the events published: