001package org.junit.rules;
002
003import static java.lang.String.format;
004import static org.hamcrest.CoreMatchers.containsString;
005import static org.hamcrest.CoreMatchers.instanceOf;
006import static org.junit.Assert.assertThat;
007import static org.junit.Assert.fail;
008import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
009import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
010import org.hamcrest.Matcher;
011import org.hamcrest.StringDescription;
012import org.junit.AssumptionViolatedException;
013import org.junit.runners.model.Statement;
014
015/**
016 * The {@code ExpectedException} rule allows you to verify that your code
017 * throws a specific exception.
018 *
019 * <h3>Usage</h3>
020 *
021 * <pre> public class SimpleExpectedExceptionTest {
022 *     &#064;Rule
023 *     public ExpectedException thrown = ExpectedException.none();
024 *
025 *     &#064;Test
026 *     public void throwsNothing() {
027 *         // no exception expected, none thrown: passes.
028 *     }
029 *
030 *     &#064;Test
031 *     public void throwsExceptionWithSpecificType() {
032 *         thrown.expect(NullPointerException.class);
033 *         throw new NullPointerException();
034 *     }
035 * }</pre>
036 * 
037 * <p>You have to add the {@code ExpectedException} rule to your test.
038 * This doesn't affect your existing tests (see {@code throwsNothing()}).
039 * After specifying the type of the expected exception your test is
040 * successful when such an exception is thrown and it fails if a
041 * different or no exception is thrown.
042 *
043 * <p>This rule does not perform any special magic to make execution continue
044 * as if the exception had not been thrown. So it is nearly always a mistake
045 * for a test method to have statements after the one that is expected to
046 * throw the exception.
047 *
048 * <p>Instead of specifying the exception's type you can characterize the
049 * expected exception based on other criteria, too:
050 *
051 * <ul>
052 *   <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li>
053 *   <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li>
054 *   <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li>
055 *   <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li>
056 * </ul>
057 *
058 * <p>You can combine any of the presented expect-methods. The test is
059 * successful if all specifications are met.
060 * <pre> &#064;Test
061 * public void throwsException() {
062 *     thrown.expect(NullPointerException.class);
063 *     thrown.expectMessage(&quot;happened&quot;);
064 *     throw new NullPointerException(&quot;What happened?&quot;);
065 * }</pre>
066 *
067 * <p>It is recommended to set the {@link org.junit.Rule#order() order} of the
068 * {@code ExpectedException} to {@code Integer.MAX_VALUE} if it is used together
069 * with another rule that handles exceptions, e.g. {@link ErrorCollector}.
070 * Otherwise failing tests may be successful.
071 * <pre> &#064;Rule(order = Integer.MAX_VALUE)
072 * public ExpectedException thrown = ExpectedException.none();</pre>
073 *
074 * <h3>AssumptionViolatedExceptions</h3>
075 * <p>JUnit uses {@link AssumptionViolatedException}s for indicating that a test
076 * provides no useful information. (See {@link org.junit.Assume} for more
077 * information.) You have to call {@code assume} methods before you set
078 * expectations of the {@code ExpectedException} rule. In this case the rule
079 * will not handle consume the exceptions and it can be handled by the
080 * framework. E.g. the following test is ignored by JUnit's default runner.
081 *
082 * <pre> &#064;Test
083 * public void ignoredBecauseOfFailedAssumption() {
084 *     assumeTrue(false); // throws AssumptionViolatedException
085 *     thrown.expect(NullPointerException.class);
086 * }</pre>
087 *
088 * <h3>AssertionErrors</h3>
089 *
090 * <p>JUnit uses {@link AssertionError}s for indicating that a test is failing. You
091 * have to call {@code assert} methods before you set expectations of the
092 * {@code ExpectedException} rule, if they should be handled by the framework.
093 * E.g. the following test fails because of the {@code assertTrue} statement.
094 *
095 * <pre> &#064;Test
096 * public void throwsUnhandled() {
097 *     assertTrue(false); // throws AssertionError
098 *     thrown.expect(NullPointerException.class);
099 * }</pre>
100 *
101 * <h3>Missing Exceptions</h3>
102 * <p>By default missing exceptions are reported with an error message
103 * like "Expected test to throw an instance of foo". You can configure a different
104 * message by means of {@link #reportMissingExceptionWithMessage(String)}. You
105 * can use a {@code %s} placeholder for the description of the expected
106 * exception. E.g. "Test doesn't throw %s." will fail with the error message
107 * "Test doesn't throw an instance of foo.".
108 *
109 * @since 4.7
110 */
111public class ExpectedException implements TestRule {
112    /**
113     * Returns a {@linkplain TestRule rule} that expects no exception to
114     * be thrown (identical to behavior without this rule).
115     *
116     * @deprecated Since 4.13
117     * {@link org.junit.Assert#assertThrows(Class, org.junit.function.ThrowingRunnable)
118     * Assert.assertThrows} can be used to verify that your code throws a specific
119     * exception.
120     */
121    @Deprecated
122    public static ExpectedException none() {
123        return new ExpectedException();
124    }
125
126    private final ExpectedExceptionMatcherBuilder matcherBuilder = new ExpectedExceptionMatcherBuilder();
127
128    private String missingExceptionMessage= "Expected test to throw %s";
129
130    private ExpectedException() {
131    }
132
133    /**
134     * This method does nothing. Don't use it.
135     * @deprecated AssertionErrors are handled by default since JUnit 4.12. Just
136     *             like in JUnit &lt;= 4.10.
137     */
138    @Deprecated
139    public ExpectedException handleAssertionErrors() {
140        return this;
141    }
142
143    /**
144     * This method does nothing. Don't use it.
145     * @deprecated AssumptionViolatedExceptions are handled by default since
146     *             JUnit 4.12. Just like in JUnit &lt;= 4.10.
147     */
148    @Deprecated
149    public ExpectedException handleAssumptionViolatedExceptions() {
150        return this;
151    }
152
153    /**
154     * Specifies the failure message for tests that are expected to throw 
155     * an exception but do not throw any. You can use a {@code %s} placeholder for
156     * the description of the expected exception. E.g. "Test doesn't throw %s."
157     * will fail with the error message
158     * "Test doesn't throw an instance of foo.".
159     *
160     * @param message exception detail message
161     * @return the rule itself
162     */
163    public ExpectedException reportMissingExceptionWithMessage(String message) {
164        missingExceptionMessage = message;
165        return this;
166    }
167
168    public Statement apply(Statement base,
169            org.junit.runner.Description description) {
170        return new ExpectedExceptionStatement(base);
171    }
172
173    /**
174     * Verify that your code throws an exception that is matched by
175     * a Hamcrest matcher.
176     * <pre> &#064;Test
177     * public void throwsExceptionThatCompliesWithMatcher() {
178     *     NullPointerException e = new NullPointerException();
179     *     thrown.expect(is(e));
180     *     throw e;
181     * }</pre>
182     */
183    public void expect(Matcher<?> matcher) {
184        matcherBuilder.add(matcher);
185    }
186
187    /**
188     * Verify that your code throws an exception that is an
189     * instance of specific {@code type}.
190     * <pre> &#064;Test
191     * public void throwsExceptionWithSpecificType() {
192     *     thrown.expect(NullPointerException.class);
193     *     throw new NullPointerException();
194     * }</pre>
195     */
196    public void expect(Class<? extends Throwable> type) {
197        expect(instanceOf(type));
198    }
199
200    /**
201     * Verify that your code throws an exception whose message contains
202     * a specific text.
203     * <pre> &#064;Test
204     * public void throwsExceptionWhoseMessageContainsSpecificText() {
205     *     thrown.expectMessage(&quot;happened&quot;);
206     *     throw new NullPointerException(&quot;What happened?&quot;);
207     * }</pre>
208     */
209    public void expectMessage(String substring) {
210        expectMessage(containsString(substring));
211    }
212
213    /**
214     * Verify that your code throws an exception whose message is matched 
215     * by a Hamcrest matcher.
216     * <pre> &#064;Test
217     * public void throwsExceptionWhoseMessageCompliesWithMatcher() {
218     *     thrown.expectMessage(startsWith(&quot;What&quot;));
219     *     throw new NullPointerException(&quot;What happened?&quot;);
220     * }</pre>
221     */
222    public void expectMessage(Matcher<String> matcher) {
223        expect(hasMessage(matcher));
224    }
225
226    /**
227     * Verify that your code throws an exception whose cause is matched by 
228     * a Hamcrest matcher.
229     * <pre> &#064;Test
230     * public void throwsExceptionWhoseCauseCompliesWithMatcher() {
231     *     NullPointerException expectedCause = new NullPointerException();
232     *     thrown.expectCause(is(expectedCause));
233     *     throw new IllegalArgumentException(&quot;What happened?&quot;, cause);
234     * }</pre>
235     */
236    public void expectCause(Matcher<?> expectedCause) {
237        expect(hasCause(expectedCause));
238    }
239
240    /**
241     * Check if any Exception is expected.
242     * @since 4.13
243     */
244    public final boolean isAnyExceptionExpected() {
245        return matcherBuilder.expectsThrowable();
246    }
247
248    private class ExpectedExceptionStatement extends Statement {
249        private final Statement next;
250
251        public ExpectedExceptionStatement(Statement base) {
252            next = base;
253        }
254
255        @Override
256        public void evaluate() throws Throwable {
257            try {
258                next.evaluate();
259            } catch (Throwable e) {
260                handleException(e);
261                return;
262            }
263            if (isAnyExceptionExpected()) {
264                failDueToMissingException();
265            }
266        }
267    }
268
269    private void handleException(Throwable e) throws Throwable {
270        if (isAnyExceptionExpected()) {
271            assertThat(e, matcherBuilder.build());
272        } else {
273            throw e;
274        }
275    }
276
277    private void failDueToMissingException() throws AssertionError {
278        fail(missingExceptionMessage());
279    }
280    
281    private String missingExceptionMessage() {
282        String expectation= StringDescription.toString(matcherBuilder.build());
283        return format(missingExceptionMessage, expectation);
284    }
285}