Skip to content

Commit cf5d0e6

Browse files
committed
Introduce publishEvent() convenience method in TestContext
This commit introduces a publishEvent() method in the TestContext API as a convenience for publishing an ApplicationEvent to the test's ApplicationContext but only if the ApplicationContext is currently available and with lazy creation of the ApplicationEvent. For example, the beforeTestClass() method in EventPublishingTestExecutionListener is now implemented as follows. public void beforeTestClass(TestContext testContext) { testContext.publishEvent(BeforeTestClassEvent::new); } Closes gh-22765
1 parent a7425c8 commit cf5d0e6

File tree

5 files changed

+188
-49
lines changed

5 files changed

+188
-49
lines changed

spring-test/src/main/java/org/springframework/test/context/TestContext.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import java.io.Serializable;
2020
import java.lang.reflect.Method;
21+
import java.util.function.Function;
2122

2223
import org.springframework.context.ApplicationContext;
24+
import org.springframework.context.ApplicationEvent;
2325
import org.springframework.core.AttributeAccessor;
2426
import org.springframework.lang.Nullable;
2527
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
@@ -76,6 +78,23 @@ default boolean hasApplicationContext() {
7678
*/
7779
ApplicationContext getApplicationContext();
7880

81+
/**
82+
* Publish the {@link ApplicationEvent} created by the given {@code eventFactory}
83+
* to the {@linkplain ApplicationContext application context} for this
84+
* test context.
85+
* <p>The {@code ApplicationEvent} will only be published if the application
86+
* context for this test context {@linkplain #hasApplicationContext() is available}.
87+
* @param eventFactory factory for lazy creation of the {@code ApplicationEvent}
88+
* @since 5.2
89+
* @see #hasApplicationContext()
90+
* @see #getApplicationContext()
91+
*/
92+
default void publishEvent(Function<TestContext, ? extends ApplicationEvent> eventFactory) {
93+
if (hasApplicationContext()) {
94+
getApplicationContext().publishEvent(eventFactory.apply(this));
95+
}
96+
}
97+
7998
/**
8099
* Get the {@linkplain Class test class} for this test context.
81100
* @return the test class (never {@code null})

spring-test/src/main/java/org/springframework/test/context/event/EventPublishingTestExecutionListener.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.test.context.event;
1818

19-
import java.util.function.Function;
20-
2119
import org.springframework.test.context.TestContext;
2220
import org.springframework.test.context.TestExecutionListener;
2321
import org.springframework.test.context.support.AbstractTestExecutionListener;
@@ -92,7 +90,7 @@ public final int getOrder() {
9290
*/
9391
@Override
9492
public void beforeTestClass(TestContext testContext) {
95-
publishEvent(testContext, BeforeTestClassEvent::new);
93+
testContext.publishEvent(BeforeTestClassEvent::new);
9694
}
9795

9896
/**
@@ -101,7 +99,7 @@ public void beforeTestClass(TestContext testContext) {
10199
*/
102100
@Override
103101
public void prepareTestInstance(TestContext testContext) {
104-
publishEvent(testContext, PrepareTestInstanceEvent::new);
102+
testContext.publishEvent(PrepareTestInstanceEvent::new);
105103
}
106104

107105
/**
@@ -110,7 +108,7 @@ public void prepareTestInstance(TestContext testContext) {
110108
*/
111109
@Override
112110
public void beforeTestMethod(TestContext testContext) {
113-
publishEvent(testContext, BeforeTestMethodEvent::new);
111+
testContext.publishEvent(BeforeTestMethodEvent::new);
114112
}
115113

116114
/**
@@ -119,7 +117,7 @@ public void beforeTestMethod(TestContext testContext) {
119117
*/
120118
@Override
121119
public void beforeTestExecution(TestContext testContext) {
122-
publishEvent(testContext, BeforeTestExecutionEvent::new);
120+
testContext.publishEvent(BeforeTestExecutionEvent::new);
123121
}
124122

125123
/**
@@ -128,7 +126,7 @@ public void beforeTestExecution(TestContext testContext) {
128126
*/
129127
@Override
130128
public void afterTestExecution(TestContext testContext) {
131-
publishEvent(testContext, AfterTestExecutionEvent::new);
129+
testContext.publishEvent(AfterTestExecutionEvent::new);
132130
}
133131

134132
/**
@@ -137,7 +135,7 @@ public void afterTestExecution(TestContext testContext) {
137135
*/
138136
@Override
139137
public void afterTestMethod(TestContext testContext) {
140-
publishEvent(testContext, AfterTestMethodEvent::new);
138+
testContext.publishEvent(AfterTestMethodEvent::new);
141139
}
142140

143141
/**
@@ -146,13 +144,7 @@ public void afterTestMethod(TestContext testContext) {
146144
*/
147145
@Override
148146
public void afterTestClass(TestContext testContext) {
149-
publishEvent(testContext, AfterTestClassEvent::new);
150-
}
151-
152-
private void publishEvent(TestContext testContext, Function<TestContext, TestContextEvent> eventFactory) {
153-
if (testContext.hasApplicationContext()) {
154-
testContext.getApplicationContext().publishEvent(eventFactory.apply(testContext));
155-
}
147+
testContext.publishEvent(AfterTestClassEvent::new);
156148
}
157149

158150
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.event;
18+
19+
import java.lang.reflect.Method;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
27+
import org.springframework.context.ApplicationEvent;
28+
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.context.event.EventListener;
30+
import org.springframework.test.context.TestContext;
31+
import org.springframework.test.context.TestExecutionListener;
32+
import org.springframework.test.context.TestExecutionListeners;
33+
import org.springframework.test.context.event.CustomTestEventTests.CustomEventPublishingTestExecutionListener;
34+
import org.springframework.test.context.junit4.SpringRunner;
35+
36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
38+
39+
/**
40+
* Integration tests for custom event publication via
41+
* {@link TestContext#publishEvent(java.util.function.Function)}.
42+
*
43+
* @author Sam Brannen
44+
* @since 5.2
45+
*/
46+
@RunWith(SpringRunner.class)
47+
@TestExecutionListeners(listeners = CustomEventPublishingTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
48+
public class CustomTestEventTests {
49+
50+
private static final List<CustomEvent> events = new ArrayList<>();
51+
52+
53+
@Before
54+
public void clearEvents() {
55+
events.clear();
56+
}
57+
58+
@Test
59+
public void customTestEventPublished() {
60+
assertThat(events).size().isEqualTo(1);
61+
CustomEvent customEvent = events.get(0);
62+
assertThat(customEvent.getSource()).isEqualTo(getClass());
63+
assertThat(customEvent.getTestName()).isEqualTo("customTestEventPublished");
64+
}
65+
66+
67+
@Configuration
68+
static class Config {
69+
70+
@EventListener
71+
void processCustomEvent(CustomEvent event) {
72+
events.add(event);
73+
}
74+
}
75+
76+
@SuppressWarnings("serial")
77+
static class CustomEvent extends ApplicationEvent {
78+
79+
private final Method testMethod;
80+
81+
82+
public CustomEvent(Class<?> testClass, Method testMethod) {
83+
super(testClass);
84+
this.testMethod = testMethod;
85+
}
86+
87+
String getTestName() {
88+
return this.testMethod.getName();
89+
}
90+
}
91+
92+
static class CustomEventPublishingTestExecutionListener implements TestExecutionListener {
93+
94+
@Override
95+
public void beforeTestExecution(TestContext testContext) throws Exception {
96+
testContext.publishEvent(tc -> new CustomEvent(tc.getTestClass(), tc.getTestMethod()));
97+
}
98+
}
99+
100+
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,13 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
7777

7878
private final TestContextManager testContextManager = new TestContextManager(ExampleTestCase.class);
7979
private final TestContext testContext = testContextManager.getTestContext();
80+
// Note that the following invocation of getApplicationContext() forces eager
81+
// loading of the test's ApplicationContext which consequently results in the
82+
// publication of all test execution events. Otherwise, TestContext#publishEvent
83+
// would never fire any events for ExampleTestCase.
8084
private final TestExecutionListener listener = testContext.getApplicationContext().getBean(TestExecutionListener.class);
8185
private final Object testInstance = new ExampleTestCase();
82-
private final Method testMethod = ReflectionUtils.findMethod(ExampleTestCase.class, "traceableTest");
86+
private final Method traceableTestMethod = ReflectionUtils.findMethod(ExampleTestCase.class, "traceableTest");
8387

8488
@Rule
8589
public final ExpectedException exception = ExpectedException.none();
@@ -104,7 +108,7 @@ public void prepareTestInstanceAnnotation() throws Exception {
104108

105109
@Test
106110
public void beforeTestMethodAnnotation() throws Exception {
107-
testContextManager.beforeTestMethod(testInstance, testMethod);
111+
testContextManager.beforeTestMethod(testInstance, traceableTestMethod);
108112
verify(listener, only()).beforeTestMethod(testContext);
109113
}
110114

@@ -162,19 +166,19 @@ public void beforeTestMethodAnnotationWithFailingAsyncEventListener() throws Exc
162166

163167
@Test
164168
public void beforeTestExecutionAnnotation() throws Exception {
165-
testContextManager.beforeTestExecution(testInstance, testMethod);
169+
testContextManager.beforeTestExecution(testInstance, traceableTestMethod);
166170
verify(listener, only()).beforeTestExecution(testContext);
167171
}
168172

169173
@Test
170174
public void afterTestExecutionAnnotation() throws Exception {
171-
testContextManager.afterTestExecution(testInstance, testMethod, null);
175+
testContextManager.afterTestExecution(testInstance, traceableTestMethod, null);
172176
verify(listener, only()).afterTestExecution(testContext);
173177
}
174178

175179
@Test
176180
public void afterTestMethodAnnotation() throws Exception {
177-
testContextManager.afterTestMethod(testInstance, testMethod, null);
181+
testContextManager.afterTestMethod(testInstance, traceableTestMethod, null);
178182
verify(listener, only()).afterTestMethod(testContext);
179183
}
180184

0 commit comments

Comments
 (0)