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 * @Rule
024 * public ExpectedException thrown= ExpectedException.none();
025 *
026 * @Test
027 * public void throwsNothing() {
028 * // no exception expected, none thrown: passes.
029 * }
030 *
031 * @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> @Test
060 * public void throwsException() {
061 * thrown.expect(NullPointerException.class);
062 * thrown.expectMessage("happened");
063 * throw new NullPointerException("What happened?");
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> @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> @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 <= 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 }