View Javadoc
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 }