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 * @Rule
24 * public ExpectedException thrown= ExpectedException.none();
25 *
26 * @Test
27 * public void throwsNothing() {
28 * // no exception expected, none thrown: passes.
29 * }
30 *
31 * @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> @Test
60 * public void throwsException() {
61 * thrown.expect(NullPointerException.class);
62 * thrown.expectMessage("happened");
63 * throw new NullPointerException("What happened?");
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> @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> @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 <= 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 <= 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> @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> @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> @Test
193 * public void throwsExceptionWhoseMessageContainsSpecificText() {
194 * thrown.expectMessage("happened");
195 * throw new NullPointerException("What happened?");
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> @Test
206 * public void throwsExceptionWhoseMessageCompliesWithMatcher() {
207 * thrown.expectMessage(startsWith("What"));
208 * throw new NullPointerException("What happened?");
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> @Test
219 * public void throwsExceptionWhoseCauseCompliesWithMatcher() {
220 * NullPointerException expectedCause = new NullPointerException();
221 * thrown.expectCause(is(expectedCause));
222 * throw new IllegalArgumentException("What happened?", 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 }