• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
none()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 
ExpectedException()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
handleAssertionErrors()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
handleAssumptionViolatedExceptions()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      */
reportMissingExceptionWithMessage(String message)152     public ExpectedException reportMissingExceptionWithMessage(String message) {
153         missingExceptionMessage = message;
154         return this;
155     }
156 
apply(Statement base, org.junit.runner.Description description)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      */
expect(Matcher<?> matcher)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      */
expect(Class<? extends Throwable> type)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      */
expectMessage(String substring)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      */
expectMessage(Matcher<String> matcher)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      */
expectCause(Matcher<? extends Throwable> expectedCause)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 
ExpectedExceptionStatement(Statement base)232         public ExpectedExceptionStatement(Statement base) {
233             next = base;
234         }
235 
236         @Override
evaluate()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 
handleException(Throwable e)250     private void handleException(Throwable e) throws Throwable {
251         if (isAnyExceptionExpected()) {
252             assertThat(e, matcherBuilder.build());
253         } else {
254             throw e;
255         }
256     }
257 
isAnyExceptionExpected()258     private boolean isAnyExceptionExpected() {
259         return matcherBuilder.expectsThrowable();
260     }
261 
failDueToMissingException()262     private void failDueToMissingException() throws AssertionError {
263         fail(missingExceptionMessage());
264     }
265 
missingExceptionMessage()266     private String missingExceptionMessage() {
267         String expectation= StringDescription.toString(matcherBuilder.build());
268         return format(missingExceptionMessage, expectation);
269     }
270 }
271