Register EventPublishingTestExecutionListener by default (round 2)

This commit registers the EventPublishingTestExecutionListener as a
default TestExecutionListener with an order of 10,000. This registers
the EventPublishingTestExecutionListener as the last listener provided
by the Spring Framework.

With EventPublishingTestExecutionListener registered with an order of
10,000, it is effectively wrapped by all other Spring listeners,
including support for @DirtiesContext and test-managed transactions.

Furthermore, this commit revises the implementation of
EventPublishingTestExecutionListener to take advantage of the new
TestContext#hasApplicationContext() support which allows the
EventPublishingTestExecutionListener to publish events only if the
test's ApplicationContext is currently available. This avoids
undesirable side-effects such as eager loading of the
ApplicationContext before it is needed or re-loading of the
ApplicationContext after it has been intentionally closed.

Closes gh-18490
This commit is contained in:
Sam Brannen
2019-04-06 15:33:47 +02:00
parent c3d0459a4e
commit 353e092bf6
9 changed files with 148 additions and 71 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.junit.Test;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.test.context.event.EventPublishingTestExecutionListener;
import org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
@@ -57,7 +58,7 @@ public class TestExecutionListenersTests {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class);
SqlScriptsTestExecutionListener.class, EventPublishingTestExecutionListener.class);
assertRegisteredListeners(DefaultListenersTestCase.class, expected);
}
@@ -69,7 +70,7 @@ public class TestExecutionListenersTests {
List<Class<?>> expected = asList(QuuxTestExecutionListener.class, ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class);
SqlScriptsTestExecutionListener.class, EventPublishingTestExecutionListener.class);
assertRegisteredListeners(MergedDefaultListenersWithCustomListenerPrependedTestCase.class, expected);
}
@@ -81,7 +82,8 @@ public class TestExecutionListenersTests {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class, BazTestExecutionListener.class);
SqlScriptsTestExecutionListener.class, EventPublishingTestExecutionListener.class,
BazTestExecutionListener.class);
assertRegisteredListeners(MergedDefaultListenersWithCustomListenerAppendedTestCase.class, expected);
}
@@ -93,7 +95,8 @@ public class TestExecutionListenersTests {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
BarTestExecutionListener.class, DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class);
TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class,
EventPublishingTestExecutionListener.class);
assertRegisteredListeners(MergedDefaultListenersWithCustomListenerInsertedTestCase.class, expected);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@@ -30,7 +29,11 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import static org.junit.Assert.*;
import static org.springframework.test.context.cache.ContextCacheTestUtils.*;
@@ -44,7 +47,6 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
* @author Sam Brannen
* @since 3.0
*/
@RunWith(JUnit4.class)
public class ClassLevelDirtiesContextTests {
private static final AtomicInteger cacheHits = new AtomicInteger(0);
@@ -146,6 +148,14 @@ public class ClassLevelDirtiesContextTests {
@RunWith(SpringRunner.class)
@ContextConfiguration
// Ensure that we do not include the EventPublishingTestExecutionListener
// since it will access the ApplicationContext for each method in the
// TestExecutionListener API, thus distorting our cache hit/miss results.
@TestExecutionListeners({
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class
})
static abstract class BaseTestCase {
@Configuration

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,12 +22,15 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import static org.junit.Assert.*;
import static org.springframework.test.context.cache.ContextCacheTestUtils.*;
@@ -37,7 +40,6 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*;
* @author Sam Brannen
* @since 4.3
*/
@RunWith(JUnit4.class)
public class DirtiesContextInterfaceTests {
private static final AtomicInteger cacheHits = new AtomicInteger(0);
@@ -72,6 +74,14 @@ public class DirtiesContextInterfaceTests {
@RunWith(SpringRunner.class)
// Ensure that we do not include the EventPublishingTestExecutionListener
// since it will access the ApplicationContext for each method in the
// TestExecutionListener API, thus distorting our cache hit/miss results.
@TestExecutionListeners({
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class
})
public static class ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase
implements DirtiesContextTestInterface {

View File

@@ -41,7 +41,6 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextManager;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.event.annotation.AfterTestClass;
import org.springframework.test.context.event.annotation.AfterTestExecution;
import org.springframework.test.context.event.annotation.AfterTestMethod;
@@ -193,7 +192,6 @@ public class EventPublishingTestExecutionListenerIntegrationTests {
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestEventListenerConfiguration.class)
@TestExecutionListeners(EventPublishingTestExecutionListener.class)
public static class ExampleTestCase {
@Traceable

View File

@@ -16,7 +16,12 @@
package org.springframework.test.context.event;
import java.util.function.Consumer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
@@ -26,16 +31,20 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.context.TestContext;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link EventPublishingTestExecutionListener}.
*
* @author Frank Scheffler
* @author Sam Brannen
* @since 5.2
*/
@RunWith(MockitoJUnitRunner.class)
@@ -49,53 +58,97 @@ public class EventPublishingTestExecutionListenerTests {
@Captor
private ArgumentCaptor<TestContextEvent> testExecutionEvent;
@Rule
public final TestName testName = new TestName();
@Before
public void configureMock() {
if (testName.getMethodName().startsWith("publish")) {
when(testContext.hasApplicationContext()).thenReturn(true);
}
}
@Test
public void publishBeforeTestClassEvent() {
listener.beforeTestClass(testContext);
assertEvent(BeforeTestClassEvent.class);
assertEvent(BeforeTestClassEvent.class, listener::beforeTestClass);
}
@Test
public void publishPrepareTestInstanceEvent() {
listener.prepareTestInstance(testContext);
assertEvent(PrepareTestInstanceEvent.class);
assertEvent(PrepareTestInstanceEvent.class, listener::prepareTestInstance);
}
@Test
public void publishBeforeTestMethodEvent() {
listener.beforeTestMethod(testContext);
assertEvent(BeforeTestMethodEvent.class);
assertEvent(BeforeTestMethodEvent.class, listener::beforeTestMethod);
}
@Test
public void publishBeforeTestExecutionEvent() {
listener.beforeTestExecution(testContext);
assertEvent(BeforeTestExecutionEvent.class);
assertEvent(BeforeTestExecutionEvent.class, listener::beforeTestExecution);
}
@Test
public void publishAfterTestExecutionEvent() {
listener.afterTestExecution(testContext);
assertEvent(AfterTestExecutionEvent.class);
assertEvent(AfterTestExecutionEvent.class, listener::afterTestExecution);
}
@Test
public void publishAfterTestMethodEvent() {
listener.afterTestMethod(testContext);
assertEvent(AfterTestMethodEvent.class);
assertEvent(AfterTestMethodEvent.class, listener::afterTestMethod);
}
@Test
public void publishAfterTestClassEvent() {
listener.afterTestClass(testContext);
assertEvent(AfterTestClassEvent.class);
assertEvent(AfterTestClassEvent.class, listener::afterTestClass);
}
private void assertEvent(Class<? extends TestContextEvent> eventClass) {
private void assertEvent(Class<? extends TestContextEvent> eventClass, Consumer<TestContext> callback) {
callback.accept(testContext);
verify(testContext.getApplicationContext(), only()).publishEvent(testExecutionEvent.capture());
assertThat(testExecutionEvent.getValue(), instanceOf(eventClass));
assertThat(testExecutionEvent.getValue().getSource(), equalTo(testContext));
}
@Test
public void doesNotPublishBeforeTestClassEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::beforeTestClass);
}
@Test
public void doesNotPublishPrepareTestInstanceEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::prepareTestInstance);
}
@Test
public void doesNotPublishBeforeTestMethodEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::beforeTestMethod);
}
@Test
public void doesNotPublishBeforeTestExecutionEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::beforeTestExecution);
}
@Test
public void doesNotPublishAfterTestExecutionEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::afterTestExecution);
}
@Test
public void doesNotPublishAfterTestMethodEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::afterTestMethod);
}
@Test
public void doesNotPublishAfterTestClassEventIfApplicationContextHasNotBeenLoaded() {
assertNoEvent(listener::afterTestClass);
}
private void assertNoEvent(Consumer<TestContext> callback) {
callback.accept(testContext);
verify(testContext.getApplicationContext(), never()).publishEvent(any());
}
}