Home | History | Annotate | Download | only in rules
      1 package org.junit.rules;
      2 
      3 import static java.lang.String.format;
      4 import static org.hamcrest.CoreMatchers.containsString;
      5 import static org.hamcrest.CoreMatchers.instanceOf;
      6 import static org.junit.Assert.assertThat;
      7 import static org.junit.Assert.fail;
      8 import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
      9 import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
     10 
     11 import org.hamcrest.Matcher;
     12 import org.hamcrest.StringDescription;
     13 import org.junit.AssumptionViolatedException;
     14 import org.junit.runners.model.Statement;
     15 
     16 /**
     17  * The {@code ExpectedException} rule allows you to verify that your code
     18  * throws a specific exception.
     19  *
     20  * <h3>Usage</h3>
     21  *
     22  * <pre> public class SimpleExpectedExceptionTest {
     23  *     &#064;Rule
     24  *     public ExpectedException thrown= ExpectedException.none();
     25  *
     26  *     &#064;Test
     27  *     public void throwsNothing() {
     28  *         // no exception expected, none thrown: passes.
     29  *     }
     30  *
     31  *     &#064;Test
     32  *     public void throwsExceptionWithSpecificType() {
     33  *         thrown.expect(NullPointerException.class);
     34  *         throw new NullPointerException();
     35  *     }
     36  * }</pre>
     37  *
     38  * <p>
     39  * You have to add the {@code ExpectedException} rule to your test.
     40  * This doesn't affect your existing tests (see {@code throwsNothing()}).
     41  * After specifiying the type of the expected exception your test is
     42  * successful when such an exception is thrown and it fails if a
     43  * different or no exception is thrown.
     44  *
     45  * <p>
     46  * Instead of specifying the exception's type you can characterize the
     47  * expected exception based on other criterias, too:
     48  *
     49  * <ul>
     50  *   <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li>
     51  *   <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li>
     52  *   <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li>
     53  *   <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li>
     54  * </ul>
     55  *
     56  * <p>
     57  * You can combine any of the presented expect-methods. The test is
     58  * successful if all specifications are met.
     59  * <pre> &#064;Test
     60  * public void throwsException() {
     61  *     thrown.expect(NullPointerException.class);
     62  *     thrown.expectMessage(&quot;happened&quot;);
     63  *     throw new NullPointerException(&quot;What happened?&quot;);
     64  * }</pre>
     65  *
     66  * <h3>AssumptionViolatedExceptions</h3>
     67  * <p>
     68  * JUnit uses {@link AssumptionViolatedException}s for indicating that a test
     69  * provides no useful information. (See {@link org.junit.Assume} for more
     70  * information.) You have to call {@code assume} methods before you set
     71  * expectations of the {@code ExpectedException} rule. In this case the rule
     72  * will not handle consume the exceptions and it can be handled by the
     73  * framework. E.g. the following test is ignored by JUnit's default runner.
     74  *
     75  * <pre> &#064;Test
     76  * public void ignoredBecauseOfFailedAssumption() {
     77  *     assumeTrue(false); // throws AssumptionViolatedException
     78  *     thrown.expect(NullPointerException.class);
     79  * }</pre>
     80  *
     81  * <h3>AssertionErrors</h3>
     82  *
     83  * <p>
     84  * JUnit uses {@link AssertionError}s for indicating that a test is failing. You
     85  * have to call {@code assert} methods before you set expectations of the
     86  * {@code ExpectedException} rule, if they should be handled by the framework.
     87  * E.g. the following test fails because of the {@code assertTrue} statement.
     88  *
     89  * <pre> &#064;Test
     90  * public void throwsUnhandled() {
     91  *     assertTrue(false); // throws AssertionError
     92  *     thrown.expect(NullPointerException.class);
     93  * }</pre>
     94  *
     95  * <h3>Missing Exceptions</h3>
     96  * <p>
     97  * By default missing exceptions are reported with an error message
     98  * like "Expected test to throw an instance of foo". You can configure a different
     99  * message by means of {@link #reportMissingExceptionWithMessage(String)}. You
    100  * can use a {@code %s} placeholder for the description of the expected
    101  * exception. E.g. "Test doesn't throw %s." will fail with the error message
    102  * "Test doesn't throw an instance of foo.".
    103  *
    104  * @since 4.7
    105  */
    106 public class ExpectedException implements TestRule {
    107     /**
    108      * Returns a {@linkplain TestRule rule} that expects no exception to
    109      * be thrown (identical to behavior without this rule).
    110      */
    111     public static ExpectedException none() {
    112         return new ExpectedException();
    113     }
    114 
    115     private final ExpectedExceptionMatcherBuilder matcherBuilder = new ExpectedExceptionMatcherBuilder();
    116 
    117     private String missingExceptionMessage= "Expected test to throw %s";
    118 
    119     private ExpectedException() {
    120     }
    121 
    122     /**
    123      * This method does nothing. Don't use it.
    124      * @deprecated AssertionErrors are handled by default since JUnit 4.12. Just
    125      *             like in JUnit &lt;= 4.10.
    126      */
    127     @Deprecated
    128     public ExpectedException handleAssertionErrors() {
    129         return this;
    130     }
    131 
    132     /**
    133      * This method does nothing. Don't use it.
    134      * @deprecated AssumptionViolatedExceptions are handled by default since
    135      *             JUnit 4.12. Just like in JUnit &lt;= 4.10.
    136      */
    137     @Deprecated
    138     public ExpectedException handleAssumptionViolatedExceptions() {
    139         return this;
    140     }
    141 
    142     /**
    143      * Specifies the failure message for tests that are expected to throw
    144      * an exception but do not throw any. You can use a {@code %s} placeholder for
    145      * the description of the expected exception. E.g. "Test doesn't throw %s."
    146      * will fail with the error message
    147      * "Test doesn't throw an instance of foo.".
    148      *
    149      * @param message exception detail message
    150      * @return the rule itself
    151      */
    152     public ExpectedException reportMissingExceptionWithMessage(String message) {
    153         missingExceptionMessage = message;
    154         return this;
    155     }
    156 
    157     public Statement apply(Statement base,
    158             org.junit.runner.Description description) {
    159         return new ExpectedExceptionStatement(base);
    160     }
    161 
    162     /**
    163      * Verify that your code throws an exception that is matched by
    164      * a Hamcrest matcher.
    165      * <pre> &#064;Test
    166      * public void throwsExceptionThatCompliesWithMatcher() {
    167      *     NullPointerException e = new NullPointerException();
    168      *     thrown.expect(is(e));
    169      *     throw e;
    170      * }</pre>
    171      */
    172     public void expect(Matcher<?> matcher) {
    173         matcherBuilder.add(matcher);
    174     }
    175 
    176     /**
    177      * Verify that your code throws an exception that is an
    178      * instance of specific {@code type}.
    179      * <pre> &#064;Test
    180      * public void throwsExceptionWithSpecificType() {
    181      *     thrown.expect(NullPointerException.class);
    182      *     throw new NullPointerException();
    183      * }</pre>
    184      */
    185     public void expect(Class<? extends Throwable> type) {
    186         expect(instanceOf(type));
    187     }
    188 
    189     /**
    190      * Verify that your code throws an exception whose message contains
    191      * a specific text.
    192      * <pre> &#064;Test
    193      * public void throwsExceptionWhoseMessageContainsSpecificText() {
    194      *     thrown.expectMessage(&quot;happened&quot;);
    195      *     throw new NullPointerException(&quot;What happened?&quot;);
    196      * }</pre>
    197      */
    198     public void expectMessage(String substring) {
    199         expectMessage(containsString(substring));
    200     }
    201 
    202     /**
    203      * Verify that your code throws an exception whose message is matched
    204      * by a Hamcrest matcher.
    205      * <pre> &#064;Test
    206      * public void throwsExceptionWhoseMessageCompliesWithMatcher() {
    207      *     thrown.expectMessage(startsWith(&quot;What&quot;));
    208      *     throw new NullPointerException(&quot;What happened?&quot;);
    209      * }</pre>
    210      */
    211     public void expectMessage(Matcher<String> matcher) {
    212         expect(hasMessage(matcher));
    213     }
    214 
    215     /**
    216      * Verify that your code throws an exception whose cause is matched by
    217      * a Hamcrest matcher.
    218      * <pre> &#064;Test
    219      * public void throwsExceptionWhoseCauseCompliesWithMatcher() {
    220      *     NullPointerException expectedCause = new NullPointerException();
    221      *     thrown.expectCause(is(expectedCause));
    222      *     throw new IllegalArgumentException(&quot;What happened?&quot;, cause);
    223      * }</pre>
    224      */
    225     public void expectCause(Matcher<? extends Throwable> expectedCause) {
    226         expect(hasCause(expectedCause));
    227     }
    228 
    229     private class ExpectedExceptionStatement extends Statement {
    230         private final Statement next;
    231 
    232         public ExpectedExceptionStatement(Statement base) {
    233             next = base;
    234         }
    235 
    236         @Override
    237         public void evaluate() throws Throwable {
    238             try {
    239                 next.evaluate();
    240             } catch (Throwable e) {
    241                 handleException(e);
    242                 return;
    243             }
    244             if (isAnyExceptionExpected()) {
    245                 failDueToMissingException();
    246             }
    247         }
    248     }
    249 
    250     private void handleException(Throwable e) throws Throwable {
    251         if (isAnyExceptionExpected()) {
    252             assertThat(e, matcherBuilder.build());
    253         } else {
    254             throw e;
    255         }
    256     }
    257 
    258     private boolean isAnyExceptionExpected() {
    259         return matcherBuilder.expectsThrowable();
    260     }
    261 
    262     private void failDueToMissingException() throws AssertionError {
    263         fail(missingExceptionMessage());
    264     }
    265 
    266     private String missingExceptionMessage() {
    267         String expectation= StringDescription.toString(matcherBuilder.build());
    268         return format(missingExceptionMessage, expectation);
    269     }
    270 }
    271