diff options
Diffstat (limited to 'src/main/java/org/junit/rules/ExpectedException.java')
-rw-r--r-- | src/main/java/org/junit/rules/ExpectedException.java | 382 |
1 files changed, 124 insertions, 258 deletions
diff --git a/src/main/java/org/junit/rules/ExpectedException.java b/src/main/java/org/junit/rules/ExpectedException.java index 4d61712..bac2fba 100644 --- a/src/main/java/org/junit/rules/ExpectedException.java +++ b/src/main/java/org/junit/rules/ExpectedException.java @@ -1,270 +1,136 @@ package org.junit.rules; -import static java.lang.String.format; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause; -import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage; - +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.AssumptionViolatedException; +import org.junit.Assert; +import org.junit.internal.matchers.TypeSafeMatcher; import org.junit.runners.model.Statement; /** - * The {@code ExpectedException} rule allows you to verify that your code - * throws a specific exception. - * - * <h3>Usage</h3> - * - * <pre> public class SimpleExpectedExceptionTest { - * @Rule - * public ExpectedException thrown= ExpectedException.none(); - * - * @Test - * public void throwsNothing() { - * // no exception expected, none thrown: passes. - * } - * - * @Test - * public void throwsExceptionWithSpecificType() { - * thrown.expect(NullPointerException.class); - * throw new NullPointerException(); - * } - * }</pre> + * The ExpectedException Rule allows in-test specification of expected exception + * types and messages: + * + * <pre> + * // These tests all pass. + * public static class HasExpectedException { + * @Rule + * public ExpectedException thrown= ExpectedException.none(); + * + * @Test + * public void throwsNothing() { + * // no exception expected, none thrown: passes. + * } * - * <p> - * You have to add the {@code ExpectedException} rule to your test. - * This doesn't affect your existing tests (see {@code throwsNothing()}). - * After specifiying the type of the expected exception your test is - * successful when such an exception is thrown and it fails if a - * different or no exception is thrown. - * - * <p> - * Instead of specifying the exception's type you can characterize the - * expected exception based on other criterias, too: - * - * <ul> - * <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li> - * <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li> - * <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li> - * <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li> - * </ul> - * - * <p> - * You can combine any of the presented expect-methods. The test is - * successful if all specifications are met. - * <pre> @Test - * public void throwsException() { - * thrown.expect(NullPointerException.class); - * thrown.expectMessage("happened"); - * throw new NullPointerException("What happened?"); - * }</pre> - * - * <h3>AssumptionViolatedExceptions</h3> - * <p> - * JUnit uses {@link AssumptionViolatedException}s for indicating that a test - * provides no useful information. (See {@link org.junit.Assume} for more - * information.) You have to call {@code assume} methods before you set - * expectations of the {@code ExpectedException} rule. In this case the rule - * will not handle consume the exceptions and it can be handled by the - * framework. E.g. the following test is ignored by JUnit's default runner. - * - * <pre> @Test - * public void ignoredBecauseOfFailedAssumption() { - * assumeTrue(false); // throws AssumptionViolatedException - * thrown.expect(NullPointerException.class); - * }</pre> - * - * <h3>AssertionErrors</h3> - * - * <p> - * JUnit uses {@link AssertionError}s for indicating that a test is failing. You - * have to call {@code assert} methods before you set expectations of the - * {@code ExpectedException} rule, if they should be handled by the framework. - * E.g. the following test fails because of the {@code assertTrue} statement. - * - * <pre> @Test - * public void throwsUnhandled() { - * assertTrue(false); // throws AssertionError - * thrown.expect(NullPointerException.class); - * }</pre> - * - * <h3>Missing Exceptions</h3> - * <p> - * By default missing exceptions are reported with an error message - * like "Expected test to throw an instance of foo". You can configure a different - * message by means of {@link #reportMissingExceptionWithMessage(String)}. You - * can use a {@code %s} placeholder for the description of the expected - * exception. E.g. "Test doesn't throw %s." will fail with the error message - * "Test doesn't throw an instance of foo.". - * - * @since 4.7 + * @Test + * public void throwsNullPointerException() { + * thrown.expect(NullPointerException.class); + * throw new NullPointerException(); + * } + * + * @Test + * public void throwsNullPointerExceptionWithMessage() { + * thrown.expect(NullPointerException.class); + * thrown.expectMessage("happened?"); + * thrown.expectMessage(startsWith("What")); + * throw new NullPointerException("What happened?"); + * } + * } + * </pre> */ public class ExpectedException implements TestRule { - /** - * Returns a {@linkplain TestRule rule} that expects no exception to - * be thrown (identical to behavior without this rule). - */ - public static ExpectedException none() { - return new ExpectedException(); - } - - private final ExpectedExceptionMatcherBuilder matcherBuilder = new ExpectedExceptionMatcherBuilder(); - - private String missingExceptionMessage= "Expected test to throw %s"; - - private ExpectedException() { - } - - /** - * This method does nothing. Don't use it. - * @deprecated AssertionErrors are handled by default since JUnit 4.12. Just - * like in JUnit <= 4.10. - */ - @Deprecated - public ExpectedException handleAssertionErrors() { - return this; - } - - /** - * This method does nothing. Don't use it. - * @deprecated AssumptionViolatedExceptions are handled by default since - * JUnit 4.12. Just like in JUnit <= 4.10. - */ - @Deprecated - public ExpectedException handleAssumptionViolatedExceptions() { - return this; - } - - /** - * Specifies the failure message for tests that are expected to throw - * an exception but do not throw any. You can use a {@code %s} placeholder for - * the description of the expected exception. E.g. "Test doesn't throw %s." - * will fail with the error message - * "Test doesn't throw an instance of foo.". - * - * @param message exception detail message - * @return the rule itself - */ - public ExpectedException reportMissingExceptionWithMessage(String message) { - missingExceptionMessage = message; - return this; - } - - public Statement apply(Statement base, - org.junit.runner.Description description) { - return new ExpectedExceptionStatement(base); - } - - /** - * Verify that your code throws an exception that is matched by - * a Hamcrest matcher. - * <pre> @Test - * public void throwsExceptionThatCompliesWithMatcher() { - * NullPointerException e = new NullPointerException(); - * thrown.expect(is(e)); - * throw e; - * }</pre> - */ - public void expect(Matcher<?> matcher) { - matcherBuilder.add(matcher); - } - - /** - * Verify that your code throws an exception that is an - * instance of specific {@code type}. - * <pre> @Test - * public void throwsExceptionWithSpecificType() { - * thrown.expect(NullPointerException.class); - * throw new NullPointerException(); - * }</pre> - */ - public void expect(Class<? extends Throwable> type) { - expect(instanceOf(type)); - } - - /** - * Verify that your code throws an exception whose message contains - * a specific text. - * <pre> @Test - * public void throwsExceptionWhoseMessageContainsSpecificText() { - * thrown.expectMessage("happened"); - * throw new NullPointerException("What happened?"); - * }</pre> - */ - public void expectMessage(String substring) { - expectMessage(containsString(substring)); - } - - /** - * Verify that your code throws an exception whose message is matched - * by a Hamcrest matcher. - * <pre> @Test - * public void throwsExceptionWhoseMessageCompliesWithMatcher() { - * thrown.expectMessage(startsWith("What")); - * throw new NullPointerException("What happened?"); - * }</pre> - */ - public void expectMessage(Matcher<String> matcher) { - expect(hasMessage(matcher)); - } - - /** - * Verify that your code throws an exception whose cause is matched by - * a Hamcrest matcher. - * <pre> @Test - * public void throwsExceptionWhoseCauseCompliesWithMatcher() { - * NullPointerException expectedCause = new NullPointerException(); - * thrown.expectCause(is(expectedCause)); - * throw new IllegalArgumentException("What happened?", cause); - * }</pre> - */ - public void expectCause(Matcher<? extends Throwable> expectedCause) { - expect(hasCause(expectedCause)); - } - - private class ExpectedExceptionStatement extends Statement { - private final Statement next; - - public ExpectedExceptionStatement(Statement base) { - next = base; - } - - @Override - public void evaluate() throws Throwable { - try { - next.evaluate(); - } catch (Throwable e) { - handleException(e); - return; - } - if (isAnyExceptionExpected()) { - failDueToMissingException(); - } - } - } - - private void handleException(Throwable e) throws Throwable { - if (isAnyExceptionExpected()) { - assertThat(e, matcherBuilder.build()); - } else { - throw e; - } - } - - private boolean isAnyExceptionExpected() { - return matcherBuilder.expectsThrowable(); - } - - private void failDueToMissingException() throws AssertionError { - fail(missingExceptionMessage()); - } - - private String missingExceptionMessage() { - String expectation= StringDescription.toString(matcherBuilder.build()); - return format(missingExceptionMessage, expectation); - } + /** + * @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 ExpectedException() { + + } + + 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. + */ + // Should be able to remove this suppression in some brave new hamcrest world. + @SuppressWarnings("unchecked") + public void expect(Matcher<?> matcher) { + if (fMatcher == null) + fMatcher= (Matcher<Object>) matcher; + else + fMatcher= both(fMatcher).and(matcher); + } + + /** + * 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} + */ + public void expectMessage(String substring) { + expectMessage(containsString(substring)); + } + + /** + * 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)); + } + + private class ExpectedExceptionStatement extends Statement { + private final Statement fNext; + + public ExpectedExceptionStatement(Statement base) { + fNext= base; + } + + @Override + public void evaluate() throws Throwable { + try { + fNext.evaluate(); + } catch (Throwable e) { + if (fMatcher == null) + throw e; + Assert.assertThat(e, fMatcher); + return; + } + if (fMatcher != null) + throw new AssertionError("Expected test to throw " + + StringDescription.toString(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()); + } + }; + } } |