001    package org.junit.rules;
002    
003    import static java.lang.String.format;
004    import static org.hamcrest.CoreMatchers.containsString;
005    import static org.hamcrest.CoreMatchers.instanceOf;
006    import static org.junit.Assert.assertThat;
007    import static org.junit.Assert.fail;
008    import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
009    import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
010    
011    import org.hamcrest.Matcher;
012    import org.hamcrest.StringDescription;
013    import org.junit.AssumptionViolatedException;
014    import 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     */
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    }