Support suppressed exceptions in the TestContext framework
Prior to this commit, if multiple TestExecutionListener 'after' methods threw an exception, only the first such exception was rethrown. Subsequent exceptions were logged, but there was no way to access or process them other than via the log file. This commit addresses this shortcoming by making use of the support for suppressed exceptions introduced in Java 7. Specifically, if multiple TestExecutionListener 'after' methods throw an exception, the first exception will be rethrown with subsequent exceptions suppressed in the first one. Issue: SPR-14459
This commit is contained in:
parent
5f176f50d3
commit
7ec85a3c3b
|
|
@ -368,6 +368,9 @@ public class TestContextManager {
|
|||
if (afterTestExecutionException == null) {
|
||||
afterTestExecutionException = ex;
|
||||
}
|
||||
else {
|
||||
afterTestExecutionException.addSuppressed(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (afterTestExecutionException != null) {
|
||||
|
|
@ -423,6 +426,9 @@ public class TestContextManager {
|
|||
if (afterTestMethodException == null) {
|
||||
afterTestMethodException = ex;
|
||||
}
|
||||
else {
|
||||
afterTestMethodException.addSuppressed(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (afterTestMethodException != null) {
|
||||
|
|
@ -464,6 +470,9 @@ public class TestContextManager {
|
|||
if (afterTestClassException == null) {
|
||||
afterTestClassException = ex;
|
||||
}
|
||||
else {
|
||||
afterTestClassException.addSuppressed(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (afterTestClassException != null) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* JUnit 4 based unit tests for {@link TestContextManager}, which verify proper
|
||||
* support for <em>suppressed exceptions</em> thrown by {@link TestExecutionListener
|
||||
* TestExecutionListeners}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.0
|
||||
* @see Throwable#getSuppressed()
|
||||
*/
|
||||
public class TestContextManagerSuppressedExceptionsTests {
|
||||
|
||||
@Test
|
||||
public void afterTestExecution() throws Exception {
|
||||
test("afterTestExecution", FailingAfterTestExecutionTestCase.class,
|
||||
(tcm, c, m) -> tcm.afterTestExecution(this, m, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterTestMethod() throws Exception {
|
||||
test("afterTestMethod", FailingAfterTestMethodTestCase.class,
|
||||
(tcm, c, m) -> tcm.afterTestMethod(this, m, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterTestClass() throws Exception {
|
||||
test("afterTestClass", FailingAfterTestClassTestCase.class, (tcm, c, m) -> tcm.afterTestClass());
|
||||
}
|
||||
|
||||
private void test(String useCase, Class<?> testClass, Callback callback) throws Exception {
|
||||
TestContextManager testContextManager = new TestContextManager(testClass);
|
||||
assertEquals("Registered TestExecutionListeners", 2, testContextManager.getTestExecutionListeners().size());
|
||||
|
||||
try {
|
||||
Method testMethod = getClass().getMethod("toString");
|
||||
callback.invoke(testContextManager, testClass, testMethod);
|
||||
fail("should have thrown an AssertionError");
|
||||
}
|
||||
catch (AssertionError err) {
|
||||
// 'after' callbacks are reversed, so 2 comes before 1.
|
||||
assertEquals(useCase + "-2", err.getMessage());
|
||||
Throwable[] suppressed = err.getSuppressed();
|
||||
assertEquals(1, suppressed.length);
|
||||
assertEquals(useCase + "-1", suppressed[0].getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Callback {
|
||||
|
||||
void invoke(TestContextManager tcm, Class<?> clazz, Method method) throws Exception;
|
||||
}
|
||||
|
||||
private static class FailingAfterTestClassListener1 implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestClass(TestContext testContext) {
|
||||
fail("afterTestClass-1");
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingAfterTestClassListener2 implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestClass(TestContext testContext) {
|
||||
fail("afterTestClass-2");
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingAfterTestMethodListener1 implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) {
|
||||
fail("afterTestMethod-1");
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingAfterTestMethodListener2 implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) {
|
||||
fail("afterTestMethod-2");
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingAfterTestExecutionListener1 implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestExecution(TestContext testContext) {
|
||||
fail("afterTestExecution-1");
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingAfterTestExecutionListener2 implements TestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void afterTestExecution(TestContext testContext) {
|
||||
fail("afterTestExecution-2");
|
||||
}
|
||||
}
|
||||
|
||||
@TestExecutionListeners({ FailingAfterTestExecutionListener1.class, FailingAfterTestExecutionListener2.class })
|
||||
private static class FailingAfterTestExecutionTestCase {
|
||||
}
|
||||
|
||||
@TestExecutionListeners({ FailingAfterTestMethodListener1.class, FailingAfterTestMethodListener2.class })
|
||||
private static class FailingAfterTestMethodTestCase {
|
||||
}
|
||||
|
||||
@TestExecutionListeners({ FailingAfterTestClassListener1.class, FailingAfterTestClassListener2.class })
|
||||
private static class FailingAfterTestClassTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue