The infrastructure behind @RecordApplicationEvents
currently uses a ThreadLocal
to capture application events published by the current thread. Unfortunately, this rules out events published by asynchronous event listeners in turn. It would be nice if events would be captured that are published on threads spawned by the current execution thread.
Comment From: sbrannen
Related Issues
- https://github.com/spring-projects-experimental/spring-modulith/issues/116
Comment From: simonbasle
The model falls apart for concurrent tests, and most notably for tests cases that instantiate the Test Class once. Typically, JUnit5 tests with @TestInstance(Lifecycle.PER_CLASS)
(and TestNG as well if I understand correctly).
To be clear, turning the ApplicationEventsHolder
ThreadLocal
into an InheritableThreadLocal
breaks the above case (as demonstrated by ParallelApplicationEventsIntegrationTests
).
Unlike in Spring Modulith, we can't really assume that tests are not parallelized (or at least parallelized in a way that events collection won't get polluted), so we have to chose between:
1. support concurrent tests / Lifecycle.PER_CLASS
2. support asynchronously published events and events assertions performed in a separate thread (like with Awaitility)
The @Autowired
support is also problematic, because for PER_CLASS
the ApplicationEvents
is injected once and cannot have a dedicated instance per test method.
This can't be solved in time for 6.0.5, and in fact I'm not sure this can be solved at all without some breaking change / behavior (and as such 6.1.0 would potentially be a better candidate). Removing from milestone and marking for further team discussion / design.
Comment From: simonbasle
I've made quite the number of local iterations on this one, and I think the minimum viable implementation is one that:
- have
SpringExtension
detect and reject test cases annotated with@RecordApplicationEvents
and@TestInstance(Lifecycle.PER_CLASS)
and@Execution(ExecutionMode.CONCURRENT)
- have
ApplicationEventsHolder
use anInheritableThreadLocal<ApplicationEvents>
I've tried various other configurations, none of which really alleviate the PER_CLASS caveat. These other ideas included: storing an ApplicationEvents
in the TestContext
, attempting to register one ApplicationEventApplicationListener
per test and to trigger autowiring again, filtering captured events in the listener if a different ApplicationEvents
could be found in the holder vs the one with which the event listener was constructed...
In the end, the InheritableThreadLocal
seems to have the same impact with the minimum of code.
Comment From: simonbasle
Superseded by gh-30020