-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ensure exceptions from methodBlock() don't result in unrooted tests.
The introduction of the runLeaf() method in BlockJUnit4ClassRunner in JUnit 4.9 introduced a regression with regard to exception handling. Specifically, the invocation of methodBlock() is no longer executed within a try-catch block as was the case in previous versions of JUnit. Custom modifications to methodBlock() or the methods it invokes may in fact throw exceptions. In such cases, exceptions thrown from methodBlock() cause the current test execution to abort immediately. As a result, the failing test method is unrooted in test reports, and subsequent test methods are never invoked. Furthermore, RunListeners registered with JUnit are not notified. This commit addresses this issue by wrapping the invocation of methodBlock() within a try-catch block. If an exception is not thrown, the resulting Statement is passed to runLeaf(). If an exception is thrown, it is wrapped in a Fail statement which is passed to runLeaf(). Closes #1066 Closes #1082
- Loading branch information
Showing
3 changed files
with
105 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
src/test/java/org/junit/runners/CustomBlockJUnit4ClassRunnerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package org.junit.runners; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Test; | ||
import org.junit.runner.Description; | ||
import org.junit.runner.notification.Failure; | ||
import org.junit.runner.notification.RunListener; | ||
import org.junit.runner.notification.RunNotifier; | ||
import org.junit.runners.model.FrameworkMethod; | ||
import org.junit.runners.model.InitializationError; | ||
import org.junit.runners.model.Statement; | ||
|
||
/** | ||
* Tests that verify proper behavior for custom runners that extend | ||
* {@link BlockJUnit4ClassRunner}. | ||
* | ||
* @author Sam Brannen | ||
* @since 4.13 | ||
*/ | ||
public class CustomBlockJUnit4ClassRunnerTest { | ||
|
||
@Test | ||
public void exceptionsFromMethodBlockMustNotResultInUnrootedTests() throws Exception { | ||
TrackingRunListener listener = new TrackingRunListener(); | ||
RunNotifier notifier = new RunNotifier(); | ||
notifier.addListener(listener); | ||
|
||
new CustomBlockJUnit4ClassRunner(CustomBlockJUnit4ClassRunnerTestCase.class).run(notifier); | ||
assertEquals("tests started.", 2, listener.testStartedCount.get()); | ||
assertEquals("tests failed.", 1, listener.testFailureCount.get()); | ||
assertEquals("tests finished.", 2, listener.testFinishedCount.get()); | ||
} | ||
|
||
|
||
public static class CustomBlockJUnit4ClassRunnerTestCase { | ||
@Test public void shouldPass() { /* no-op */ } | ||
@Test public void throwException() { /* no-op */ } | ||
} | ||
|
||
/** | ||
* Custom extension of {@link BlockJUnit4ClassRunner} that always throws | ||
* an exception from the {@code methodBlock()} if a test method is named | ||
* exactly {@code "throwException"}. | ||
*/ | ||
private static class CustomBlockJUnit4ClassRunner extends BlockJUnit4ClassRunner { | ||
|
||
CustomBlockJUnit4ClassRunner(Class<?> testClass) throws InitializationError { | ||
super(testClass); | ||
} | ||
|
||
@Override | ||
protected Statement methodBlock(FrameworkMethod method) { | ||
if ("throwException".equals(method.getName())) { | ||
throw new RuntimeException("throwException() test method invoked"); | ||
} | ||
return super.methodBlock(method); | ||
} | ||
} | ||
|
||
/** | ||
* Simple {@link RunListener} that tracks the number of times that | ||
* certain callbacks are invoked. | ||
*/ | ||
private static class TrackingRunListener extends RunListener { | ||
|
||
final AtomicInteger testStartedCount = new AtomicInteger(); | ||
final AtomicInteger testFailureCount = new AtomicInteger(); | ||
final AtomicInteger testFinishedCount = new AtomicInteger(); | ||
|
||
|
||
@Override | ||
public void testStarted(Description description) throws Exception { | ||
testStartedCount.incrementAndGet(); | ||
} | ||
|
||
@Override | ||
public void testFailure(Failure failure) throws Exception { | ||
testFailureCount.incrementAndGet(); | ||
} | ||
|
||
@Override | ||
public void testFinished(Description description) throws Exception { | ||
testFinishedCount.incrementAndGet(); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters