Skip to content

Commit

Permalink
Merge pull request junit-team#323 from stefanbirkner/assumption
Browse files Browse the repository at this point in the history
Fixes junit-teamgh-121 (ExpectedException handles JUnit exceptions)
  • Loading branch information
dsaff committed Apr 9, 2012
2 parents 3a5c9f2 + f3ae021 commit c4279e4
Show file tree
Hide file tree
Showing 5 changed files with 567 additions and 254 deletions.
102 changes: 82 additions & 20 deletions src/main/java/org/junit/rules/ExpectedException.java
@@ -1,17 +1,18 @@
package org.junit.rules;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.both;
import static org.junit.matchers.JUnitMatchers.containsString;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.junit.Assert;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.matchers.TypeSafeMatcher;
import org.junit.runners.model.Statement;

/**
* The ExpectedException Rule allows in-test specification of expected exception
* The ExpectedException rule allows in-test specification of expected exception
* types and messages:
*
* <pre>
Expand All @@ -22,7 +23,7 @@
*
* &#064;Test
* public void throwsNothing() {
* // no exception expected, none thrown: passes.
* // no exception expected, none thrown: passes.
* }
*
* &#064;Test
Expand All @@ -40,31 +41,74 @@
* }
* }
* </pre>
*
* By default ExpectedException rule doesn't handle AssertionErrors and
* AssumptionViolatedExceptions, because such exceptions are used by JUnit. If
* you want to handle such exceptions you have to call @link
* {@link #handleAssertionErrors()} or @link
* {@link #handleAssumptionViolatedExceptions()}.
*
* <pre>
* // These tests all pass.
* public static class HasExpectedException {
* &#064;Rule
* public ExpectedException thrown= ExpectedException.none();
*
* &#064;Test
* public void throwExpectedAssertionError() {
* thrown.handleAssertionErrors();
* thrown.expect(AssertionError.class);
* throw new AssertionError();
* }
*
* &#064;Test
* public void throwExpectAssumptionViolatedException() {
* thrown.handleAssumptionViolatedExceptions();
* thrown.expect(AssumptionViolatedException.class);
* throw new AssumptionViolatedException(&quot;&quot;);
* }
* }
* </pre>
*/
public class ExpectedException implements TestRule {
/**
* @return a Rule that expects no exception to be thrown
* (identical to behavior without this Rule)
* @return a Rule that expects no exception to be thrown (identical to
* behavior without this Rule)
*/
public static ExpectedException none() {
return new ExpectedException();
}

private Matcher<Object> fMatcher= null;

private boolean handleAssumptionViolatedExceptions= false;

private boolean handleAssertionErrors= false;

private ExpectedException() {

}


public ExpectedException handleAssertionErrors() {
handleAssertionErrors= true;
return this;
}

public ExpectedException handleAssumptionViolatedExceptions() {
handleAssumptionViolatedExceptions= true;
return this;
}

public Statement apply(Statement base,
org.junit.runner.Description description) {
return new ExpectedExceptionStatement(base);
}

/**
* Adds {@code matcher} to the list of requirements for any thrown exception.
* Adds {@code matcher} to the list of requirements for any thrown
* exception.
*/
// Should be able to remove this suppression in some brave new hamcrest world.
// Should be able to remove this suppression in some brave new hamcrest
// world.
@SuppressWarnings("unchecked")
public void expect(Matcher<?> matcher) {
if (fMatcher == null)
Expand All @@ -74,24 +118,24 @@ public void expect(Matcher<?> matcher) {
}

/**
* Adds to the list of requirements for any thrown exception that it
* should be an instance of {@code type}
* Adds to the list of requirements for any thrown exception that it should
* be an instance of {@code type}
*/
public void expect(Class<? extends Throwable> type) {
expect(instanceOf(type));
}

/**
* Adds to the list of requirements for any thrown exception that it
* should <em>contain</em> string {@code substring}
* Adds to the list of requirements for any thrown exception that it should
* <em>contain</em> string {@code substring}
*/
public void expectMessage(String substring) {
expectMessage(containsString(substring));
}

/**
* Adds {@code matcher} to the list of requirements for the message
* returned from any thrown exception.
* Adds {@code matcher} to the list of requirements for the message returned
* from any thrown exception.
*/
public void expectMessage(Matcher<String> matcher) {
expect(hasMessage(matcher));
Expand All @@ -108,10 +152,14 @@ public ExpectedExceptionStatement(Statement base) {
public void evaluate() throws Throwable {
try {
fNext.evaluate();
} catch (AssumptionViolatedException e) {
optionallyHandleException(e, handleAssumptionViolatedExceptions);
return;
} catch (AssertionError e) {
optionallyHandleException(e, handleAssertionErrors);
return;
} catch (Throwable e) {
if (fMatcher == null)
throw e;
Assert.assertThat(e, fMatcher);
handleException(e);
return;
}
if (fMatcher != null)
Expand All @@ -120,17 +168,31 @@ public void evaluate() throws Throwable {
}
}

private void optionallyHandleException(Throwable e, boolean handleException)
throws Throwable {
if (handleException)
handleException(e);
else
throw e;
}

private void handleException(Throwable e) throws Throwable {
if (fMatcher == null)
throw e;
assertThat(e, fMatcher);
}

private Matcher<Throwable> hasMessage(final Matcher<String> matcher) {
return new TypeSafeMatcher<Throwable>() {
public void describeTo(Description description) {
description.appendText("exception with message ");
description.appendDescriptionOf(matcher);
}

@Override
public boolean matchesSafely(Throwable item) {
return matcher.matches(item.getMessage());
}
};
}
}
}
4 changes: 2 additions & 2 deletions src/test/java/org/junit/tests/AllTests.java
Expand Up @@ -26,7 +26,7 @@
import org.junit.tests.experimental.parallel.ParallelMethodTest;
import org.junit.tests.experimental.rules.BlockJUnit4ClassRunnerOverrideTest;
import org.junit.tests.experimental.rules.ClassRulesTest;
import org.junit.tests.experimental.rules.ExpectedExceptionRuleTest;
import org.junit.tests.experimental.rules.ExpectedExceptionTest;
import org.junit.tests.experimental.rules.ExternalResourceRuleTest;
import org.junit.tests.experimental.rules.MethodRulesTest;
import org.junit.tests.experimental.rules.NameRulesTest;
Expand Down Expand Up @@ -148,7 +148,7 @@
ParentRunnerTest.class,
NameRulesTest.class,
ClassRulesTest.class,
ExpectedExceptionRuleTest.class,
ExpectedExceptionTest.class,
TempFolderRuleTest.class,
TemporaryFolderUsageTest.class,
ExternalResourceRuleTest.class,
Expand Down
159 changes: 159 additions & 0 deletions src/test/java/org/junit/tests/experimental/rules/EventCollector.java
@@ -0,0 +1,159 @@
package org.junit.tests.experimental.rules;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.matchers.JUnitMatchers.both;

import java.util.ArrayList;
import java.util.List;

import org.hamcrest.Matcher;
import org.junit.internal.matchers.TypeSafeMatcher;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

class EventCollector extends RunListener {
static Matcher<EventCollector> everyTestRunSuccessful() {
return both(hasNoFailure()).and(hasNoAssumptionFailure());
}

private static Matcher<EventCollector> hasNumberOfFailures(
final int numberOfFailures) {
return new TypeSafeMatcher<EventCollector>() {
@Override
public boolean matchesSafely(EventCollector item) {
return item.fFailures.size() == numberOfFailures;
}

public void describeTo(org.hamcrest.Description description) {
description.appendText("has ");
description.appendValue(numberOfFailures);
description.appendText(" failures");
}
};
}

static Matcher<EventCollector> hasSingleFailure() {
return hasNumberOfFailures(1);
}

static Matcher<EventCollector> hasNoFailure() {
return hasNumberOfFailures(0);
}

private static Matcher<EventCollector> hasNumberOfAssumptionFailures(
final int numberOfFailures) {
return new TypeSafeMatcher<EventCollector>() {
@Override
public boolean matchesSafely(EventCollector item) {
return item.fAssumptionFailures.size() == numberOfFailures;
}

public void describeTo(org.hamcrest.Description description) {
description.appendText("has ");
description.appendValue(numberOfFailures);
description.appendText(" assumption failures");
}
};
}

static Matcher<EventCollector> hasSingleAssumptionFailure() {
return hasNumberOfAssumptionFailures(1);
}

static Matcher<EventCollector> hasNoAssumptionFailure() {
return hasNumberOfAssumptionFailures(0);
}

static Matcher<EventCollector> hasSingleFailureWithMessage(String message) {
return hasSingleFailureWithMessage(equalTo(message));
}

static Matcher<EventCollector> hasSingleFailureWithMessage(
final Matcher<String> messageMatcher) {
return new TypeSafeMatcher<EventCollector>() {
@Override
public boolean matchesSafely(EventCollector item) {
return hasSingleFailure().matches(item)
&& messageMatcher.matches(item.fFailures.get(0)
.getMessage());
}

public void describeTo(org.hamcrest.Description description) {
description.appendText("has single failure with message ");
messageMatcher.describeTo(description);
}
};
}

private final List<Description> fTestRunsStarted= new ArrayList<Description>();

private final List<Result> fTestRunsFinished= new ArrayList<Result>();

private final List<Description> fTestsStarted= new ArrayList<Description>();

private final List<Description> fTestsFinished= new ArrayList<Description>();

private final List<Failure> fFailures= new ArrayList<Failure>();

private final List<Failure> fAssumptionFailures= new ArrayList<Failure>();

private final List<Description> fTestsIgnored= new ArrayList<Description>();

@Override
public void testRunStarted(Description description) throws Exception {
fTestRunsStarted.add(description);
}

@Override
public void testRunFinished(Result result) throws Exception {
fTestRunsFinished.add(result);
}

@Override
public void testStarted(Description description) throws Exception {
fTestsStarted.add(description);
}

@Override
public void testFinished(Description description) throws Exception {
fTestsFinished.add(description);
}

@Override
public void testFailure(Failure failure) throws Exception {
fFailures.add(failure);
}

@Override
public void testAssumptionFailure(Failure failure) {
fAssumptionFailures.add(failure);
}

@Override
public void testIgnored(Description description) throws Exception {
fTestsIgnored.add(description);
}

@Override
public String toString() {
StringBuilder sb= new StringBuilder();
sb.append(fTestRunsStarted.size());
sb.append(" test runs started, ");
sb.append(fTestRunsFinished.size());
sb.append(" test runs finished, ");
sb.append(fTestsStarted.size());
sb.append(" tests started, ");
sb.append(fTestsFinished.size());
sb.append(" tests finished, ");
sb.append(fFailures.size());
sb.append(" failures, ");
sb.append(fAssumptionFailures.size());
sb.append(" assumption failures, ");
sb.append(fTestsIgnored.size());
sb.append(" tests ignored");

return sb.toString();
}
}

0 comments on commit c4279e4

Please sign in to comment.