Skip to content

Commit a2f02db

Browse files
committed
Improve documentation for TestContext events
This commit improves the documentation for test execution events, especially with regard to the fact that, by default, a BeforeTestClassEvent is not published for the first test class using a particular ApplicationContext. This commit also introduces tests that verify the default behavior and the ability to change the default behavior with a custom TestExecutionListener that eagerly loads the context. Closes gh-27757
1 parent 8cbb188 commit a2f02db

File tree

11 files changed

+264
-22
lines changed

11 files changed

+264
-22
lines changed

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

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,16 +17,12 @@
1717
package org.springframework.test.context.event;
1818

1919
import org.springframework.test.context.TestContext;
20-
import org.springframework.test.context.TestExecutionListener;
2120
import org.springframework.test.context.support.AbstractTestExecutionListener;
2221

2322
/**
24-
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListener}
25-
* that publishes test execution events to the
23+
* {@code TestExecutionListener} that publishes test execution events to the
2624
* {@link org.springframework.context.ApplicationContext ApplicationContext}
27-
* for the currently executing test. Events are only published if the
28-
* {@code ApplicationContext} {@linkplain TestContext#hasApplicationContext()
29-
* has already been loaded}.
25+
* for the currently executing test.
3026
*
3127
* <h3>Supported Events</h3>
3228
* <ul>
@@ -41,11 +37,33 @@
4137
*
4238
* <p>These events may be consumed for various reasons, such as resetting <em>mock</em>
4339
* beans or tracing test execution. One advantage of consuming test events rather
44-
* than implementing a custom {@link TestExecutionListener} is that test events
45-
* may be consumed by any Spring bean registered in the test {@code ApplicationContext},
46-
* and such beans may benefit directly from dependency injection and other features
47-
* of the {@code ApplicationContext}. In contrast, a {@link TestExecutionListener}
48-
* is not a bean in the {@code ApplicationContext}.
40+
* than implementing a custom {@link org.springframework.test.context.TestExecutionListener
41+
* TestExecutionListener} is that test events may be consumed by any Spring bean
42+
* registered in the test {@code ApplicationContext}, and such beans may benefit
43+
* directly from dependency injection and other features of the {@code ApplicationContext}.
44+
* In contrast, a {@code TestExecutionListener} is not a bean in the {@code ApplicationContext}.
45+
*
46+
* <p>Note that the {@code EventPublishingTestExecutionListener} is registered by
47+
* default; however, it only publishes events if the {@code ApplicationContext}
48+
* {@linkplain TestContext#hasApplicationContext() has already been loaded}. This
49+
* prevents the {@code ApplicationContext} from being loaded unnecessarily or too
50+
* early. Consequently, a {@code BeforeTestClassEvent} will not be published until
51+
* after the {@code ApplicationContext} has been loaded by another
52+
* {@code TestExecutionListener}. For example, with the default set of
53+
* {@code TestExecutionListeners} registered, a {@code BeforeTestClassEvent} will
54+
* not be published for the first test class that uses a particular test
55+
* {@code ApplicationContext}, but a {@code BeforeTestClassEvent} will be published
56+
* for any subsequent test class in the same test suite that uses the same test
57+
* {@code ApplicationContext} since the context will already have been loaded
58+
* when subsequent test classes run (as long as the context has not been removed
59+
* from the {@link org.springframework.test.context.cache.ContextCache ContextCache}
60+
* via {@link org.springframework.test.annotation.DirtiesContext @DirtiesContext}
61+
* or the max-size eviction policy). If you wish to ensure that a
62+
* {@code BeforeTestClassEvent} is published for every test class, you need to
63+
* register a {@code TestExecutionListener} that loads the {@code ApplicationContext}
64+
* in the {@link org.springframework.test.context.TestExecutionListener#beforeTestClass
65+
* beforeTestClass} callback, and that {@code TestExecutionListener} must be registered
66+
* before the {@code EventPublishingTestExecutionListener}.
4967
*
5068
* <h3>Exception Handling</h3>
5169
* <p>By default, if a test event listener throws an exception while consuming

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,8 @@
4949
* <p>The {@code EventPublishingTestExecutionListener} must be registered in order
5050
* for this annotation to have an effect &mdash; for example, via
5151
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners}.
52+
* Note, however, that the {@code EventPublishingTestExecutionListener} is registered
53+
* by default.
5254
*
5355
* @author Frank Scheffler
5456
* @author Sam Brannen
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/**
2-
* Test event annotations for the <em>Spring TestContext Framework</em>.
2+
* Test execution event annotations for the <em>Spring TestContext Framework</em>.
33
*/
44
package org.springframework.test.context.event.annotation;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Copyright 2002-2022 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.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.junit.jupiter.api.AfterEach;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.platform.testkit.engine.EngineTestKit;
26+
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.core.annotation.Order;
29+
import org.springframework.test.context.TestContext;
30+
import org.springframework.test.context.TestExecutionListener;
31+
import org.springframework.test.context.TestExecutionListeners;
32+
import org.springframework.test.context.TestExecutionListeners.MergeMode;
33+
import org.springframework.test.context.event.annotation.AfterTestClass;
34+
import org.springframework.test.context.event.annotation.AfterTestExecution;
35+
import org.springframework.test.context.event.annotation.AfterTestMethod;
36+
import org.springframework.test.context.event.annotation.BeforeTestClass;
37+
import org.springframework.test.context.event.annotation.BeforeTestExecution;
38+
import org.springframework.test.context.event.annotation.BeforeTestMethod;
39+
import org.springframework.test.context.event.annotation.PrepareTestInstance;
40+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
41+
42+
import static org.assertj.core.api.Assertions.assertThat;
43+
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
44+
45+
/**
46+
* Tests for the {@link EventPublishingTestExecutionListener} which verify that
47+
* a {@link BeforeTestClassEvent} can be eagerly published; whereas, such an
48+
* event is not published by default for the first run of a test class for a
49+
* specific {@code ApplicationContext}.
50+
*
51+
* @author Sam Brannen
52+
* @since 5.3.17
53+
* @see https://github.com/spring-projects/spring-framework/issues/27757
54+
*/
55+
class EagerTestExecutionEventPublishingTests {
56+
57+
private static final List<Class<? extends TestContextEvent>> events = new ArrayList<>();
58+
59+
60+
@BeforeEach
61+
@AfterEach
62+
void resetEvents() {
63+
events.clear();
64+
}
65+
66+
@Test
67+
void beforeTestClassEventIsNotPublishedByDefaultForFirstTestClass() {
68+
EngineTestKit.engine("junit-jupiter")//
69+
.selectors(selectClass(LazyTestCase1.class), selectClass(LazyTestCase2.class))//
70+
.execute()//
71+
.testEvents()//
72+
.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
73+
74+
assertThat(events).containsExactly(//
75+
// 1st test class
76+
// BeforeTestClassEvent.class -- missing for 1st test class
77+
PrepareTestInstanceEvent.class, //
78+
BeforeTestMethodEvent.class, //
79+
BeforeTestExecutionEvent.class, //
80+
AfterTestExecutionEvent.class, //
81+
AfterTestMethodEvent.class, //
82+
AfterTestClassEvent.class, //
83+
// 2nd test class
84+
BeforeTestClassEvent.class, //
85+
PrepareTestInstanceEvent.class, //
86+
BeforeTestMethodEvent.class, //
87+
BeforeTestExecutionEvent.class, //
88+
AfterTestExecutionEvent.class, //
89+
AfterTestMethodEvent.class, //
90+
AfterTestClassEvent.class//
91+
);
92+
}
93+
94+
@Test
95+
void beforeTestClassEventIsPublishedForAllTestClassesIfCustomListenerEagerlyLoadsContext() {
96+
EngineTestKit.engine("junit-jupiter")//
97+
.selectors(selectClass(EagerTestCase1.class), selectClass(EagerTestCase2.class))//
98+
.execute()//
99+
.testEvents()//
100+
.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
101+
102+
assertThat(events).containsExactly(//
103+
// 1st test class
104+
BeforeTestClassEvent.class, //
105+
PrepareTestInstanceEvent.class, //
106+
BeforeTestMethodEvent.class, //
107+
BeforeTestExecutionEvent.class, //
108+
AfterTestExecutionEvent.class, //
109+
AfterTestMethodEvent.class, //
110+
AfterTestClassEvent.class, //
111+
// 2nd test class
112+
BeforeTestClassEvent.class, //
113+
PrepareTestInstanceEvent.class, //
114+
BeforeTestMethodEvent.class, //
115+
BeforeTestExecutionEvent.class, //
116+
AfterTestExecutionEvent.class, //
117+
AfterTestMethodEvent.class, //
118+
AfterTestClassEvent.class//
119+
);
120+
}
121+
122+
123+
@SpringJUnitConfig(Config.class)
124+
static class LazyTestCase1 {
125+
126+
@Test
127+
void test() {
128+
}
129+
}
130+
131+
static class LazyTestCase2 extends LazyTestCase1 {
132+
}
133+
134+
@TestExecutionListeners(listeners = EagerLoadingTestExecutionListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
135+
static class EagerTestCase1 extends LazyTestCase1 {
136+
}
137+
138+
static class EagerTestCase2 extends EagerTestCase1 {
139+
}
140+
141+
@Configuration
142+
static class Config {
143+
144+
@BeforeTestClass
145+
public void beforeTestClass(BeforeTestClassEvent e) {
146+
events.add(e.getClass());
147+
}
148+
149+
@PrepareTestInstance
150+
public void prepareTestInstance(PrepareTestInstanceEvent e) {
151+
events.add(e.getClass());
152+
}
153+
154+
@BeforeTestMethod
155+
public void beforeTestMethod(BeforeTestMethodEvent e) {
156+
events.add(e.getClass());
157+
}
158+
159+
@BeforeTestExecution
160+
public void beforeTestExecution(BeforeTestExecutionEvent e) {
161+
events.add(e.getClass());
162+
}
163+
164+
@AfterTestExecution
165+
public void afterTestExecution(AfterTestExecutionEvent e) {
166+
events.add(e.getClass());
167+
}
168+
169+
@AfterTestMethod
170+
public void afterTestMethod(AfterTestMethodEvent e) {
171+
events.add(e.getClass());
172+
}
173+
174+
@AfterTestClass
175+
public void afterTestClass(AfterTestClassEvent e) {
176+
events.add(e.getClass());
177+
}
178+
179+
}
180+
181+
@Order(0)
182+
static class EagerLoadingTestExecutionListener implements TestExecutionListener {
183+
184+
@Override
185+
public void beforeTestClass(TestContext testContext) {
186+
testContext.getApplicationContext();
187+
}
188+
}
189+
190+
}

0 commit comments

Comments
 (0)