diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java index a0ed3db9c20..22fa1a136d2 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java @@ -42,8 +42,10 @@ import org.springframework.test.context.TestContextManager; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks; +import org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks; import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks; import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks; +import org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks; import org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks; import org.springframework.test.context.junit4.statements.SpringFailOnTimeout; import org.springframework.test.context.junit4.statements.SpringRepeat; @@ -270,6 +272,9 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { * Spring-specific timeouts in that the former execute in a separate * thread while the latter simply execute in the main thread (like regular * tests). + * @see #methodInvoker(FrameworkMethod, Object) + * @see #withBeforeTestExecutionCallbacks(FrameworkMethod, Object, Statement) + * @see #withAfterTestExecutionCallbacks(FrameworkMethod, Object, Statement) * @see #possiblyExpectingExceptions(FrameworkMethod, Object, Statement) * @see #withBefores(FrameworkMethod, Object, Statement) * @see #withAfters(FrameworkMethod, Object, Statement) @@ -293,6 +298,8 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { } Statement statement = methodInvoker(frameworkMethod, testInstance); + statement = withBeforeTestExecutionCallbacks(frameworkMethod, testInstance, statement); + statement = withAfterTestExecutionCallbacks(frameworkMethod, testInstance, statement); statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement); statement = withBefores(frameworkMethod, testInstance, statement); statement = withAfters(frameworkMethod, testInstance, statement); @@ -404,6 +411,26 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { return TestAnnotationUtils.getTimeout(frameworkMethod.getMethod()); } + /** + * Wrap the supplied {@link Statement} with a {@code RunBeforeTestExecutionCallbacks} + * statement, thus preserving the default functionality while adding support for the + * Spring TestContext Framework. + * @see RunBeforeTestExecutionCallbacks + */ + protected Statement withBeforeTestExecutionCallbacks(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + return new RunBeforeTestExecutionCallbacks(statement, testInstance, frameworkMethod.getMethod(), getTestContextManager()); + } + + /** + * Wrap the supplied {@link Statement} with a {@code RunAfterTestExecutionCallbacks} + * statement, thus preserving the default functionality while adding support for the + * Spring TestContext Framework. + * @see RunAfterTestExecutionCallbacks + */ + protected Statement withAfterTestExecutionCallbacks(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + return new RunAfterTestExecutionCallbacks(statement, testInstance, frameworkMethod.getMethod(), getTestContextManager()); + } + /** * Wrap the {@link Statement} returned by the parent implementation with a * {@code RunBeforeTestMethodCallbacks} statement, thus preserving the @@ -414,8 +441,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { @Override protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { Statement junitBefores = super.withBefores(frameworkMethod, testInstance, statement); - return new RunBeforeTestMethodCallbacks(junitBefores, testInstance, frameworkMethod.getMethod(), - getTestContextManager()); + return new RunBeforeTestMethodCallbacks(junitBefores, testInstance, frameworkMethod.getMethod(), getTestContextManager()); } /** @@ -428,8 +454,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { @Override protected Statement withAfters(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { Statement junitAfters = super.withAfters(frameworkMethod, testInstance, statement); - return new RunAfterTestMethodCallbacks(junitAfters, testInstance, frameworkMethod.getMethod(), - getTestContextManager()); + return new RunAfterTestMethodCallbacks(junitAfters, testInstance, frameworkMethod.getMethod(), getTestContextManager()); } /** diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java index 89a97418ac1..a8f78a0d06b 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -37,7 +37,7 @@ import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** - * {@code SpringMethodRule} is a custom JUnit {@link MethodRule} that + * {@code SpringMethodRule} is a custom JUnit 4 {@link MethodRule} that * supports instance-level and method-level features of the * Spring TestContext Framework in standard JUnit tests by means * of the {@link TestContextManager} and associated support classes and @@ -82,6 +82,12 @@ import org.springframework.util.ReflectionUtils; * *
NOTE: As of Spring Framework 4.3, this class requires JUnit 4.12 or higher. * + *
WARNING: Due to the shortcomings of JUnit rules, the + * {@code SpringMethodRule} does not support the + * {@code beforeTestExecution()} and {@code afterTestExecution()} callbacks of the + * {@link org.springframework.test.context.TestExecutionListener TestExecutionListener} + * API. + * * @author Sam Brannen * @author Philippe Marschall * @since 4.2 diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestExecutionCallbacks.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestExecutionCallbacks.java new file mode 100644 index 00000000000..818b1e8b3c3 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestExecutionCallbacks.java @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit4.statements; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; +import org.springframework.test.context.TestContextManager; + +/** + * {@code RunAfterTestExecutionCallbacks} is a custom JUnit {@link Statement} + * which allows the Spring TestContext Framework to be plugged into the + * JUnit 4 execution chain by calling {@link TestContextManager#afterTestExecution + * afterTestExecution()} on the supplied {@link TestContextManager}. + * + *
NOTE: This class requires JUnit 4.9 or higher. + * + * @author Sam Brannen + * @since 5.0 + * @see #evaluate() + * @see RunBeforeTestExecutionCallbacks + */ +public class RunAfterTestExecutionCallbacks extends Statement { + + private final Statement next; + + private final Object testInstance; + + private final Method testMethod; + + private final TestContextManager testContextManager; + + + /** + * Construct a new {@code RunAfterTestExecutionCallbacks} statement. + * @param next the next {@code Statement} in the execution chain + * @param testInstance the current test instance (never {@code null}) + * @param testMethod the test method which has just been executed on the + * test instance + * @param testContextManager the TestContextManager upon which to call + * {@code afterTestExecution()} + */ + public RunAfterTestExecutionCallbacks(Statement next, Object testInstance, Method testMethod, + TestContextManager testContextManager) { + + this.next = next; + this.testInstance = testInstance; + this.testMethod = testMethod; + this.testContextManager = testContextManager; + } + + /** + * Evaluate the next {@link Statement} in the execution chain (typically an + * instance of {@link RunBeforeTestExecutionCallbacks}), catching any exceptions + * thrown, and then invoke {@link TestContextManager#afterTestExecution} supplying + * the first caught exception (if any). + *
If the invocation of {@code afterTestExecution()} throws an exception, that
+ * exception will also be tracked. Multiple exceptions will be combined into a
+ * {@link MultipleFailureException}.
+ */
+ @Override
+ public void evaluate() throws Throwable {
+ Throwable testException = null;
+ List Indirectly, this class also verifies that all {@link TestExecutionListener}
- * lifecycle callbacks are called.
+ * See: SPR-3960.
*
- * As of Spring 3.0, this class also tests support for the new
- * {@link TestExecutionListener#beforeTestClass(TestContext) beforeTestClass()}
- * and {@link TestExecutionListener#afterTestClass(TestContext)
- * afterTestClass()} lifecycle callback methods.
+ * Indirectly, this class also verifies that all {@code TestExecutionListener}
+ * lifecycle callbacks are called.
*
* @author Sam Brannen
* @since 2.5
*/
@RunWith(Parameterized.class)
-public class FailingBeforeAndAfterMethodsJUnitTests {
+public class FailingBeforeAndAfterMethodsSpringRunnerTests {
protected final Class> clazz;
@@ -68,13 +62,15 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
AlwaysFailingAfterTestClassTestCase.class.getSimpleName(),//
AlwaysFailingPrepareTestInstanceTestCase.class.getSimpleName(),//
AlwaysFailingBeforeTestMethodTestCase.class.getSimpleName(),//
+ AlwaysFailingBeforeTestExecutionTestCase.class.getSimpleName(), //
+ AlwaysFailingAfterTestExecutionTestCase.class.getSimpleName(), //
AlwaysFailingAfterTestMethodTestCase.class.getSimpleName(),//
FailingBeforeTransactionTestCase.class.getSimpleName(),//
FailingAfterTransactionTestCase.class.getSimpleName() //
};
}
- public FailingBeforeAndAfterMethodsJUnitTests(String testClassName) throws Exception {
+ public FailingBeforeAndAfterMethodsSpringRunnerTests(String testClassName) throws Exception {
this.clazz = ClassUtils.forName(getClass().getName() + "." + testClassName, getClass().getClassLoader());
}
@@ -93,7 +89,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
// -------------------------------------------------------------------
- protected static class AlwaysFailingBeforeTestClassTestExecutionListener extends AbstractTestExecutionListener {
+ protected static class AlwaysFailingBeforeTestClassTestExecutionListener implements TestExecutionListener {
@Override
public void beforeTestClass(TestContext testContext) {
@@ -101,7 +97,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
}
- protected static class AlwaysFailingAfterTestClassTestExecutionListener extends AbstractTestExecutionListener {
+ protected static class AlwaysFailingAfterTestClassTestExecutionListener implements TestExecutionListener {
@Override
public void afterTestClass(TestContext testContext) {
@@ -109,7 +105,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
}
- protected static class AlwaysFailingPrepareTestInstanceTestExecutionListener extends AbstractTestExecutionListener {
+ protected static class AlwaysFailingPrepareTestInstanceTestExecutionListener implements TestExecutionListener {
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
@@ -117,7 +113,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
}
- protected static class AlwaysFailingBeforeTestMethodTestExecutionListener extends AbstractTestExecutionListener {
+ protected static class AlwaysFailingBeforeTestMethodTestExecutionListener implements TestExecutionListener {
@Override
public void beforeTestMethod(TestContext testContext) {
@@ -125,7 +121,15 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
}
- protected static class AlwaysFailingAfterTestMethodTestExecutionListener extends AbstractTestExecutionListener {
+ protected static class AlwaysFailingBeforeTestExecutionTestExecutionListener implements TestExecutionListener {
+
+ @Override
+ public void beforeTestExecution(TestContext testContext) {
+ fail("always failing beforeTestExecution()");
+ }
+ }
+
+ protected static class AlwaysFailingAfterTestMethodTestExecutionListener implements TestExecutionListener {
@Override
public void afterTestMethod(TestContext testContext) {
@@ -133,8 +137,15 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
}
}
+ protected static class AlwaysFailingAfterTestExecutionTestExecutionListener implements TestExecutionListener {
+
+ @Override
+ public void afterTestExecution(TestContext testContext) {
+ fail("always failing afterTestExecution()");
+ }
+ }
+
@RunWith(SpringRunner.class)
- @TestExecutionListeners({})
public static abstract class BaseTestCase {
@Test
@@ -162,6 +173,16 @@ public class FailingBeforeAndAfterMethodsJUnitTests {
public static class AlwaysFailingBeforeTestMethodTestCase extends BaseTestCase {
}
+ @Ignore("TestCase classes are run manually by the enclosing test class")
+ @TestExecutionListeners(AlwaysFailingBeforeTestExecutionTestExecutionListener.class)
+ public static class AlwaysFailingBeforeTestExecutionTestCase extends BaseTestCase {
+ }
+
+ @Ignore("TestCase classes are run manually by the enclosing test class")
+ @TestExecutionListeners(AlwaysFailingAfterTestExecutionTestExecutionListener.class)
+ public static class AlwaysFailingAfterTestExecutionTestCase extends BaseTestCase {
+ }
+
@Ignore("TestCase classes are run manually by the enclosing test class")
@TestExecutionListeners(AlwaysFailingAfterTestMethodTestExecutionListener.class)
public static class AlwaysFailingAfterTestMethodTestCase extends BaseTestCase {
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java
index aae85f1c261..d3838cb1acc 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java
@@ -27,7 +27,7 @@ import org.junit.runners.Parameterized.Parameters;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
-import org.springframework.test.context.junit4.FailingBeforeAndAfterMethodsJUnitTests;
+import org.springframework.test.context.junit4.FailingBeforeAndAfterMethodsSpringRunnerTests;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
@@ -35,14 +35,14 @@ import org.springframework.transaction.annotation.Transactional;
import static org.junit.Assert.*;
/**
- * This class is an extension of {@link FailingBeforeAndAfterMethodsJUnitTests}
+ * This class is an extension of {@link FailingBeforeAndAfterMethodsSpringRunnerTests}
* that has been modified to use {@link SpringClassRule} and
* {@link SpringMethodRule}.
*
* @author Sam Brannen
* @since 4.2
*/
-public class FailingBeforeAndAfterMethodsSpringRuleTests extends FailingBeforeAndAfterMethodsJUnitTests {
+public class FailingBeforeAndAfterMethodsSpringRuleTests extends FailingBeforeAndAfterMethodsSpringRunnerTests {
@Parameters(name = "{0}")
public static Object[] testData() {
@@ -69,7 +69,6 @@ public class FailingBeforeAndAfterMethodsSpringRuleTests extends FailingBeforeAn
// All tests are in superclass.
@RunWith(JUnit4.class)
- @TestExecutionListeners({})
public static abstract class BaseSpringRuleTestCase {
@ClassRule