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;
010
011import org.hamcrest.Matcher;
012import org.hamcrest.StringDescription;
013import org.junit.AssumptionViolatedException;
014import org.junit.runners.model.Statement;
015
016/**
017 * The {@code ExpectedException} rule allows you to verify that your code
018 * throws a specific exception.
019 *
020 * <h3>Usage</h3>
021 *
022 * <pre> public class SimpleExpectedExceptionTest {
023 *     &#064;Rule
024 *     public ExpectedException thrown= ExpectedException.none();
025 *
026 *     &#064;Test
027 *     public void throwsNothing() {
028 *         // no exception expected, none thrown: passes.
029 *     }
030 *
031 *     &#064;Test
032 *     public void throwsExceptionWithSpecificType() {
033 *         thrown.expect(NullPointerException.class);
034 *         throw new NullPointerException();
035 *     }
036 * }</pre>
037 * 
038 * <p>
039 * You have to add the {@code ExpectedException} rule to your test.
040 * This doesn't affect your existing tests (see {@code throwsNothing()}).
041 * After specifiying the type of the expected exception your test is
042 * successful when such an exception is thrown and it fails if a
043 * different or no exception is thrown.
044 *
045 * <p>
046 * Instead of specifying the exception's type you can characterize the
047 * expected exception based on other criterias, too:
048 *
049 * <ul>
050 *   <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li>
051 *   <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li>
052 *   <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li>
053 *   <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li>
054 * </ul>
055 *
056 * <p>
057 * You can combine any of the presented expect-methods. The test is
058 * successful if all specifications are met.
059 * <pre> &#064;Test
060 * public void throwsException() {
061 *     thrown.expect(NullPointerException.class);
062 *     thrown.expectMessage(&quot;happened&quot;);
063 *     throw new NullPointerException(&quot;What happened?&quot;);
064 * }</pre>
065 *
066 * <h3>AssumptionViolatedExceptions</h3>
067 * <p>
068 * JUnit uses {@link AssumptionViolatedException}s for indicating that a test
069 * provides no useful information. (See {@link org.junit.Assume} for more
070 * information.) You have to call {@code assume} methods before you set
071 * expectations of the {@code ExpectedException} rule. In this case the rule
072 * will not handle consume the exceptions and it can be handled by the
073 * framework. E.g. the following test is ignored by JUnit's default runner.
074 *
075 * <pre> &#064;Test
076 * public void ignoredBecauseOfFailedAssumption() {
077 *     assumeTrue(false); // throws AssumptionViolatedException
078 *     thrown.expect(NullPointerException.class);
079 * }</pre>
080 *
081 * <h3>AssertionErrors</h3>
082 *
083 * <p>
084 * JUnit uses {@link AssertionError}s for indicating that a test is failing. You
085 * have to call {@code assert} methods before you set expectations of the
086 * {@code ExpectedException} rule, if they should be handled by the framework.
087 * E.g. the following test fails because of the {@code assertTrue} statement.
088 *
089 * <pre> &#064;Test
090 * public void throwsUnhandled() {
091 *     assertTrue(false); // throws AssertionError
092 *     thrown.expect(NullPointerException.class);
093 * }</pre>
094 *
095 * <h3>Missing Exceptions</h3>
096 * <p>
097 * By default missing exceptions are reported with an error message
098 * like "Expected test to throw an instance of foo". You can configure a different
099 * 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 */
106public 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}