Skip to content

Commit d9c22e6

Browse files
committed
Document the effect of @DirtiesContext on test execution events
See gh-27757
1 parent 8a510db commit d9c22e6

File tree

3 files changed

+245
-1
lines changed

3 files changed

+245
-1
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@
6363
* register a {@code TestExecutionListener} that loads the {@code ApplicationContext}
6464
* in the {@link org.springframework.test.context.TestExecutionListener#beforeTestClass
6565
* beforeTestClass} callback, and that {@code TestExecutionListener} must be registered
66-
* before the {@code EventPublishingTestExecutionListener}.
66+
* before the {@code EventPublishingTestExecutionListener}. Similarly, if
67+
* {@code @DirtiesContext} is used to remove the {@code ApplicationContext} from
68+
* the context cache after the last test method in a given test class, the
69+
* {@code AfterTestClassEvent} will not be published for that test class.
6770
*
6871
* <h3>Exception Handling</h3>
6972
* <p>By default, if a test event listener throws an exception while consuming
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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.MethodOrderer.DisplayName;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.TestMethodOrder;
27+
import org.junit.platform.testkit.engine.EngineTestKit;
28+
29+
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.test.annotation.DirtiesContext;
31+
import org.springframework.test.annotation.DirtiesContext.MethodMode;
32+
import org.springframework.test.context.TestPropertySource;
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
47+
* behavior for test context events when {@link DirtiesContext @DirtiesContext}
48+
* is used.
49+
*
50+
* @author Sam Brannen
51+
* @since 5.3.17
52+
* @see https://github.com/spring-projects/spring-framework/issues/27757
53+
*/
54+
class DirtiesContextEventPublishingTests {
55+
56+
private static final List<Class<? extends TestContextEvent>> events = new ArrayList<>();
57+
58+
59+
@BeforeEach
60+
@AfterEach
61+
void resetEvents() {
62+
events.clear();
63+
}
64+
65+
@Test
66+
void classLevelDirtiesContext() {
67+
EngineTestKit.engine("junit-jupiter")//
68+
.selectors(selectClass(ClassLevelDirtiesContextTestCase.class))//
69+
.execute()//
70+
.testEvents()//
71+
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
72+
73+
assertThat(events).containsExactly(//
74+
// BeforeTestClassEvent.class -- always missing for 1st test class by default
75+
PrepareTestInstanceEvent.class, //
76+
BeforeTestMethodEvent.class, //
77+
BeforeTestExecutionEvent.class, //
78+
AfterTestExecutionEvent.class, //
79+
AfterTestMethodEvent.class, //
80+
AfterTestClassEvent.class //
81+
);
82+
}
83+
84+
@Test
85+
void methodLevelAfterMethodDirtiesContext() {
86+
EngineTestKit.engine("junit-jupiter")//
87+
.selectors(selectClass(MethodLevelAfterMethodDirtiesContextTestCase.class))//
88+
.execute()//
89+
.testEvents()//
90+
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
91+
92+
assertThat(events).containsExactly(//
93+
// BeforeTestClassEvent.class -- always missing for 1st test class by default
94+
PrepareTestInstanceEvent.class, //
95+
BeforeTestMethodEvent.class, //
96+
BeforeTestExecutionEvent.class, //
97+
AfterTestExecutionEvent.class, //
98+
AfterTestMethodEvent.class //
99+
// AfterTestClassEvent.class -- missing b/c of @DirtiestContext "after method" at the method level
100+
);
101+
}
102+
103+
@Test
104+
void methodLevelAfterMethodDirtiesContextWithSubsequentTestMethod() {
105+
EngineTestKit.engine("junit-jupiter")//
106+
.selectors(selectClass(MethodLevelAfterMethodDirtiesContextWithSubsequentTestMethodTestCase.class))//
107+
.execute()//
108+
.testEvents()//
109+
.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));
110+
111+
assertThat(events).containsExactly(//
112+
// BeforeTestClassEvent.class -- always missing for 1st test class by default
113+
// test1()
114+
PrepareTestInstanceEvent.class, //
115+
BeforeTestMethodEvent.class, //
116+
BeforeTestExecutionEvent.class, //
117+
AfterTestExecutionEvent.class, //
118+
AfterTestMethodEvent.class, //
119+
// test2()
120+
PrepareTestInstanceEvent.class, //
121+
BeforeTestMethodEvent.class, //
122+
BeforeTestExecutionEvent.class, //
123+
AfterTestExecutionEvent.class, //
124+
AfterTestMethodEvent.class, //
125+
AfterTestClassEvent.class // b/c @DirtiestContext is not applied for test2()
126+
);
127+
}
128+
129+
@Test
130+
void methodLevelBeforeMethodDirtiesContext() {
131+
EngineTestKit.engine("junit-jupiter")//
132+
.selectors(selectClass(MethodLevelBeforeMethodDirtiesContextTestCase.class))//
133+
.execute()//
134+
.testEvents()//
135+
.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));
136+
137+
assertThat(events).containsExactly(//
138+
// BeforeTestClassEvent.class -- always missing for 1st test class by default
139+
PrepareTestInstanceEvent.class, //
140+
BeforeTestMethodEvent.class, //
141+
BeforeTestExecutionEvent.class, //
142+
AfterTestExecutionEvent.class, //
143+
AfterTestMethodEvent.class, //
144+
AfterTestClassEvent.class // b/c @DirtiestContext happens "before method" at the method level
145+
);
146+
}
147+
148+
@SpringJUnitConfig(Config.class)
149+
// add unique property to get a unique ApplicationContext
150+
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = class-level")
151+
@DirtiesContext
152+
static class ClassLevelDirtiesContextTestCase {
153+
154+
@Test
155+
void test() {
156+
}
157+
}
158+
159+
@SpringJUnitConfig(Config.class)
160+
// add unique property to get a unique ApplicationContext
161+
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = method-level-after-method")
162+
static class MethodLevelAfterMethodDirtiesContextTestCase {
163+
164+
@Test
165+
@DirtiesContext
166+
void test1() {
167+
}
168+
}
169+
170+
@SpringJUnitConfig(Config.class)
171+
// add unique property to get a unique ApplicationContext
172+
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = method-level-after-method-with-subsequent-test-method")
173+
@TestMethodOrder(DisplayName.class)
174+
static class MethodLevelAfterMethodDirtiesContextWithSubsequentTestMethodTestCase {
175+
176+
@Test
177+
@DirtiesContext
178+
void test1() {
179+
}
180+
181+
@Test
182+
void test2() {
183+
}
184+
}
185+
186+
@SpringJUnitConfig(Config.class)
187+
// add unique property to get a unique ApplicationContext
188+
@TestPropertySource(properties = "DirtiesContextEventPublishingTests.key = method-level-before-method")
189+
static class MethodLevelBeforeMethodDirtiesContextTestCase {
190+
191+
@Test
192+
@DirtiesContext(methodMode = MethodMode.BEFORE_METHOD)
193+
void test() {
194+
}
195+
}
196+
197+
@Configuration
198+
static class Config {
199+
200+
@BeforeTestClass
201+
public void beforeTestClass(BeforeTestClassEvent e) {
202+
events.add(e.getClass());
203+
}
204+
205+
@PrepareTestInstance
206+
public void prepareTestInstance(PrepareTestInstanceEvent e) {
207+
events.add(e.getClass());
208+
}
209+
210+
@BeforeTestMethod
211+
public void beforeTestMethod(BeforeTestMethodEvent e) {
212+
events.add(e.getClass());
213+
}
214+
215+
@BeforeTestExecution
216+
public void beforeTestExecution(BeforeTestExecutionEvent e) {
217+
events.add(e.getClass());
218+
}
219+
220+
@AfterTestExecution
221+
public void afterTestExecution(AfterTestExecutionEvent e) {
222+
events.add(e.getClass());
223+
}
224+
225+
@AfterTestMethod
226+
public void afterTestMethod(AfterTestMethodEvent e) {
227+
events.add(e.getClass());
228+
}
229+
230+
@AfterTestClass
231+
public void afterTestClass(AfterTestClassEvent e) {
232+
events.add(e.getClass());
233+
}
234+
235+
}
236+
237+
}

src/docs/asciidoc/testing.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,10 @@ If you wish to ensure that a `BeforeTestClassEvent` is always published for ever
27252725
class, you need to register a `TestExecutionListener` that loads the `ApplicationContext`
27262726
in the `beforeTestClass` callback, and that `TestExecutionListener` must be registered
27272727
_before_ the `EventPublishingTestExecutionListener`.
2728+
2729+
Similarly, if `@DirtiesContext` is used to remove the `ApplicationContext` from the
2730+
context cache after the last test method in a given test class, the `AfterTestClassEvent`
2731+
will not be published for that test class.
27282732
====
27292733

27302734
In order to listen to test execution events, a Spring bean may choose to implement the

0 commit comments

Comments
 (0)