Skip to content

Commit 4112ca7

Browse files
committed
Separate async/sync TestContext event listeners in tests
See gh-18490
1 parent 6daf33d commit 4112ca7

File tree

1 file changed

+58
-49
lines changed

1 file changed

+58
-49
lines changed

spring-test/src/test/java/org/springframework/test/context/event/EventPublishingTestExecutionListenerIntegrationTests.java

Lines changed: 58 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public void afterTestClassAnnotation() throws Exception {
182182
}
183183

184184
@RunWith(SpringRunner.class)
185-
@ContextConfiguration(classes = EventCaptureConfiguration.class)
185+
@ContextConfiguration(classes = TestEventListenerConfiguration.class)
186186
@TestExecutionListeners(listeners = EventPublishingTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
187187
public static class ExampleTestCase {
188188

@@ -211,7 +211,7 @@ public void testWithFailingAsyncEventListener() {
211211

212212
@Configuration
213213
@EnableAsync(proxyTargetClass = true)
214-
static class EventCaptureConfiguration extends AsyncConfigurerSupport {
214+
static class TestEventListenerConfiguration extends AsyncConfigurerSupport {
215215

216216
@Override
217217
public Executor getAsyncExecutor() {
@@ -231,88 +231,97 @@ public TestExecutionListener listener() {
231231
return mock(TestExecutionListener.class);
232232
}
233233

234+
/**
235+
* The {@code @Async} test event listener method must reside in a separate
236+
* component since {@code @Async} is not supported on methods in
237+
* {@code @Configuration} classes.
238+
*/
234239
@Bean
235-
EventCaptureBean eventCaptureBean() {
236-
return new EventCaptureBean(listener());
237-
}
238-
239-
}
240-
241-
static class TrackingAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
242-
243-
static volatile Throwable asyncException;
244-
245-
246-
@Override
247-
public void handleUncaughtException(Throwable exception, Method method, Object... params) {
248-
asyncException = exception;
249-
countDownLatch.countDown();
250-
}
251-
}
252-
253-
// MUST be annotated with @Component due to a change in Spring 5.1 that
254-
// does not consider beans in a package starting with "org.springframework"
255-
// to be event listeners unless they are also components. See
256-
// org.springframework.context.event.EventListenerMethodProcessor.isSpringContainerClass(Class<?>)
257-
// for details.
258-
@Component
259-
static class EventCaptureBean {
260-
261-
final TestExecutionListener listener;
262-
263-
264-
EventCaptureBean(TestExecutionListener listener) {
265-
this.listener = listener;
240+
AsyncTestEventComponent asyncTestEventComponent() {
241+
return new AsyncTestEventComponent(listener());
266242
}
267243

268244
@BeforeTestClass("#root.event.source.testClass.name matches '.+TestCase'")
269245
public void beforeTestClass(BeforeTestClassEvent e) throws Exception {
270-
this.listener.beforeTestClass(e.getSource());
246+
listener().beforeTestClass(e.getSource());
271247
}
272248

273249
@PrepareTestInstance("#a0.testContext.testClass.name matches '.+TestCase'")
274250
public void prepareTestInstance(PrepareTestInstanceEvent e) throws Exception {
275-
this.listener.prepareTestInstance(e.getSource());
251+
listener().prepareTestInstance(e.getSource());
276252
}
277253

278254
@BeforeTestMethod("#p0.testContext.testMethod.isAnnotationPresent(T(org.springframework.test.context.event.EventPublishingTestExecutionListenerIntegrationTests.Traceable))")
279255
public void beforeTestMethod(BeforeTestMethodEvent e) throws Exception {
280-
this.listener.beforeTestMethod(e.getSource());
256+
listener().beforeTestMethod(e.getSource());
281257
}
282258

283259
@BeforeTestMethod("event.testContext.testMethod.name == 'testWithFailingEventListener'")
284260
public void beforeTestMethodWithFailure(BeforeTestMethodEvent event) throws Exception {
285-
this.listener.beforeTestMethod(event.getSource());
261+
listener().beforeTestMethod(event.getSource());
286262
throw new RuntimeException("Boom!");
287263
}
288264

289-
@BeforeTestMethod("event.testContext.testMethod.name == 'testWithFailingAsyncEventListener'")
290-
@Async
291-
public void beforeTestMethodWithAsyncFailure(BeforeTestMethodEvent event) throws Exception {
292-
this.listener.beforeTestMethod(event.getSource());
293-
throw new RuntimeException(String.format("Asynchronous exception for test method [%s] in thread [%s]",
294-
event.getTestContext().getTestMethod().getName(), Thread.currentThread().getName()));
295-
}
296-
297265
@BeforeTestExecution
298266
public void beforeTestExecution(BeforeTestExecutionEvent e) throws Exception {
299-
this.listener.beforeTestExecution(e.getSource());
267+
listener().beforeTestExecution(e.getSource());
300268
}
301269

302270
@AfterTestExecution
303271
public void afterTestExecution(AfterTestExecutionEvent e) throws Exception {
304-
this.listener.afterTestExecution(e.getSource());
272+
listener().afterTestExecution(e.getSource());
305273
}
306274

307275
@AfterTestMethod("event.testContext.testMethod.isAnnotationPresent(T(org.springframework.test.context.event.EventPublishingTestExecutionListenerIntegrationTests.Traceable))")
308276
public void afterTestMethod(AfterTestMethodEvent e) throws Exception {
309-
this.listener.afterTestMethod(e.getSource());
277+
listener().afterTestMethod(e.getSource());
310278
}
311279

312280
@AfterTestClass("#afterTestClassEvent.testContext.testClass.name matches '.+TestCase'")
313281
public void afterTestClass(AfterTestClassEvent afterTestClassEvent) throws Exception {
314-
this.listener.afterTestClass(afterTestClassEvent.getSource());
282+
listener().afterTestClass(afterTestClassEvent.getSource());
315283
}
284+
285+
}
286+
287+
/**
288+
* MUST be annotated with {@code @Component} due to a change in Spring 5.1 that
289+
* does not consider beans in a package starting with "org.springframework" to be
290+
* event listeners unless they are also components.
291+
*
292+
* @see org.springframework.context.event.EventListenerMethodProcessor#isSpringContainerClass
293+
*/
294+
@Component
295+
static class AsyncTestEventComponent {
296+
297+
final TestExecutionListener listener;
298+
299+
300+
AsyncTestEventComponent(TestExecutionListener listener) {
301+
this.listener = listener;
302+
}
303+
304+
@BeforeTestMethod("event.testContext.testMethod.name == 'testWithFailingAsyncEventListener'")
305+
@Async
306+
public void beforeTestMethodWithAsyncFailure(BeforeTestMethodEvent event) throws Exception {
307+
this.listener.beforeTestMethod(event.getSource());
308+
throw new RuntimeException(String.format("Asynchronous exception for test method [%s] in thread [%s]",
309+
event.getTestContext().getTestMethod().getName(), Thread.currentThread().getName()));
310+
}
311+
312+
}
313+
314+
static class TrackingAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
315+
316+
static volatile Throwable asyncException;
317+
318+
319+
@Override
320+
public void handleUncaughtException(Throwable exception, Method method, Object... params) {
321+
asyncException = exception;
322+
countDownLatch.countDown();
323+
}
324+
316325
}
317326

318327
}

0 commit comments

Comments
 (0)