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 import org.hamcrest.Matcher; 11 import org.hamcrest.StringDescription; 12 import org.junit.AssumptionViolatedException; 13 import org.junit.runners.model.Statement; 14 15 /** 16 * The {@code ExpectedException} rule allows you to verify that your code 17 * throws a specific exception. 18 * 19 * <h3>Usage</h3> 20 * 21 * <pre> public class SimpleExpectedExceptionTest { 22 * @Rule 23 * public ExpectedException thrown = ExpectedException.none(); 24 * 25 * @Test 26 * public void throwsNothing() { 27 * // no exception expected, none thrown: passes. 28 * } 29 * 30 * @Test 31 * public void throwsExceptionWithSpecificType() { 32 * thrown.expect(NullPointerException.class); 33 * throw new NullPointerException(); 34 * } 35 * }</pre> 36 * 37 * <p>You have to add the {@code ExpectedException} rule to your test. 38 * This doesn't affect your existing tests (see {@code throwsNothing()}). 39 * After specifying the type of the expected exception your test is 40 * successful when such an exception is thrown and it fails if a 41 * different or no exception is thrown. 42 * 43 * <p>This rule does not perform any special magic to make execution continue 44 * as if the exception had not been thrown. So it is nearly always a mistake 45 * for a test method to have statements after the one that is expected to 46 * throw the exception. 47 * 48 * <p>Instead of specifying the exception's type you can characterize the 49 * expected exception based on other criteria, too: 50 * 51 * <ul> 52 * <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li> 53 * <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li> 54 * <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li> 55 * <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li> 56 * </ul> 57 * 58 * <p>You can combine any of the presented expect-methods. The test is 59 * successful if all specifications are met. 60 * <pre> @Test 61 * public void throwsException() { 62 * thrown.expect(NullPointerException.class); 63 * thrown.expectMessage("happened"); 64 * throw new NullPointerException("What happened?"); 65 * }</pre> 66 * 67 * <p>It is recommended to set the {@link org.junit.Rule#order() order} of the 68 * {@code ExpectedException} to {@code Integer.MAX_VALUE} if it is used together 69 * with another rule that handles exceptions, e.g. {@link ErrorCollector}. 70 * Otherwise failing tests may be successful. 71 * <pre> @Rule(order = Integer.MAX_VALUE) 72 * public ExpectedException thrown = ExpectedException.none();</pre> 73 * 74 * <h3>AssumptionViolatedExceptions</h3> 75 * <p>JUnit uses {@link AssumptionViolatedException}s for indicating that a test 76 * provides no useful information. (See {@link org.junit.Assume} for more 77 * information.) You have to call {@code assume} methods before you set 78 * expectations of the {@code ExpectedException} rule. In this case the rule 79 * will not handle consume the exceptions and it can be handled by the 80 * framework. E.g. the following test is ignored by JUnit's default runner. 81 * 82 * <pre> @Test 83 * public void ignoredBecauseOfFailedAssumption() { 84 * assumeTrue(false); // throws AssumptionViolatedException 85 * thrown.expect(NullPointerException.class); 86 * }</pre> 87 * 88 * <h3>AssertionErrors</h3> 89 * 90 * <p>JUnit uses {@link AssertionError}s for indicating that a test is failing. You 91 * have to call {@code assert} methods before you set expectations of the 92 * {@code ExpectedException} rule, if they should be handled by the framework. 93 * E.g. the following test fails because of the {@code assertTrue} statement. 94 * 95 * <pre> @Test 96 * public void throwsUnhandled() { 97 * assertTrue(false); // throws AssertionError 98 * thrown.expect(NullPointerException.class); 99 * }</pre> 100 * 101 * <h3>Missing Exceptions</h3> 102 * <p>By default missing exceptions are reported with an error message 103 * like "Expected test to throw an instance of foo". You can configure a different 104 * message by means of {@link #reportMissingExceptionWithMessage(String)}. You 105 * can use a {@code %s} placeholder for the description of the expected 106 * exception. E.g. "Test doesn't throw %s." will fail with the error message 107 * "Test doesn't throw an instance of foo.". 108 * 109 * @since 4.7 110 */ 111 public class ExpectedException implements TestRule { 112 /** 113 * Returns a {@linkplain TestRule rule} that expects no exception to 114 * be thrown (identical to behavior without this rule). 115 * 116 * @deprecated Since 4.13 117 * {@link org.junit.Assert#assertThrows(Class, org.junit.function.ThrowingRunnable) 118 * Assert.assertThrows} can be used to verify that your code throws a specific 119 * exception. 120 */ 121 @Deprecated none()122 public static ExpectedException none() { 123 return new ExpectedException(); 124 } 125 126 private final ExpectedExceptionMatcherBuilder matcherBuilder = new ExpectedExceptionMatcherBuilder(); 127 128 private String missingExceptionMessage= "Expected test to throw %s"; 129 ExpectedException()130 private ExpectedException() { 131 } 132 133 /** 134 * This method does nothing. Don't use it. 135 * @deprecated AssertionErrors are handled by default since JUnit 4.12. Just 136 * like in JUnit <= 4.10. 137 */ 138 @Deprecated handleAssertionErrors()139 public ExpectedException handleAssertionErrors() { 140 return this; 141 } 142 143 /** 144 * This method does nothing. Don't use it. 145 * @deprecated AssumptionViolatedExceptions are handled by default since 146 * JUnit 4.12. Just like in JUnit <= 4.10. 147 */ 148 @Deprecated handleAssumptionViolatedExceptions()149 public ExpectedException handleAssumptionViolatedExceptions() { 150 return this; 151 } 152 153 /** 154 * Specifies the failure message for tests that are expected to throw 155 * an exception but do not throw any. You can use a {@code %s} placeholder for 156 * the description of the expected exception. E.g. "Test doesn't throw %s." 157 * will fail with the error message 158 * "Test doesn't throw an instance of foo.". 159 * 160 * @param message exception detail message 161 * @return the rule itself 162 */ reportMissingExceptionWithMessage(String message)163 public ExpectedException reportMissingExceptionWithMessage(String message) { 164 missingExceptionMessage = message; 165 return this; 166 } 167 apply(Statement base, org.junit.runner.Description description)168 public Statement apply(Statement base, 169 org.junit.runner.Description description) { 170 return new ExpectedExceptionStatement(base); 171 } 172 173 /** 174 * Verify that your code throws an exception that is matched by 175 * a Hamcrest matcher. 176 * <pre> @Test 177 * public void throwsExceptionThatCompliesWithMatcher() { 178 * NullPointerException e = new NullPointerException(); 179 * thrown.expect(is(e)); 180 * throw e; 181 * }</pre> 182 */ expect(Matcher<?> matcher)183 public void expect(Matcher<?> matcher) { 184 matcherBuilder.add(matcher); 185 } 186 187 /** 188 * Verify that your code throws an exception that is an 189 * instance of specific {@code type}. 190 * <pre> @Test 191 * public void throwsExceptionWithSpecificType() { 192 * thrown.expect(NullPointerException.class); 193 * throw new NullPointerException(); 194 * }</pre> 195 */ expect(Class<? extends Throwable> type)196 public void expect(Class<? extends Throwable> type) { 197 expect(instanceOf(type)); 198 } 199 200 /** 201 * Verify that your code throws an exception whose message contains 202 * a specific text. 203 * <pre> @Test 204 * public void throwsExceptionWhoseMessageContainsSpecificText() { 205 * thrown.expectMessage("happened"); 206 * throw new NullPointerException("What happened?"); 207 * }</pre> 208 */ expectMessage(String substring)209 public void expectMessage(String substring) { 210 expectMessage(containsString(substring)); 211 } 212 213 /** 214 * Verify that your code throws an exception whose message is matched 215 * by a Hamcrest matcher. 216 * <pre> @Test 217 * public void throwsExceptionWhoseMessageCompliesWithMatcher() { 218 * thrown.expectMessage(startsWith("What")); 219 * throw new NullPointerException("What happened?"); 220 * }</pre> 221 */ expectMessage(Matcher<String> matcher)222 public void expectMessage(Matcher<String> matcher) { 223 expect(hasMessage(matcher)); 224 } 225 226 /** 227 * Verify that your code throws an exception whose cause is matched by 228 * a Hamcrest matcher. 229 * <pre> @Test 230 * public void throwsExceptionWhoseCauseCompliesWithMatcher() { 231 * NullPointerException expectedCause = new NullPointerException(); 232 * thrown.expectCause(is(expectedCause)); 233 * throw new IllegalArgumentException("What happened?", cause); 234 * }</pre> 235 */ expectCause(Matcher<?> expectedCause)236 public void expectCause(Matcher<?> expectedCause) { 237 expect(hasCause(expectedCause)); 238 } 239 240 /** 241 * Check if any Exception is expected. 242 * @since 4.13 243 */ isAnyExceptionExpected()244 public final boolean isAnyExceptionExpected() { 245 return matcherBuilder.expectsThrowable(); 246 } 247 248 private class ExpectedExceptionStatement extends Statement { 249 private final Statement next; 250 ExpectedExceptionStatement(Statement base)251 public ExpectedExceptionStatement(Statement base) { 252 next = base; 253 } 254 255 @Override evaluate()256 public void evaluate() throws Throwable { 257 try { 258 next.evaluate(); 259 } catch (Throwable e) { 260 handleException(e); 261 return; 262 } 263 if (isAnyExceptionExpected()) { 264 failDueToMissingException(); 265 } 266 } 267 } 268 handleException(Throwable e)269 private void handleException(Throwable e) throws Throwable { 270 if (isAnyExceptionExpected()) { 271 assertThat(e, matcherBuilder.build()); 272 } else { 273 throw e; 274 } 275 } 276 failDueToMissingException()277 private void failDueToMissingException() throws AssertionError { 278 fail(missingExceptionMessage()); 279 } 280 missingExceptionMessage()281 private String missingExceptionMessage() { 282 String expectation= StringDescription.toString(matcherBuilder.build()); 283 return format(missingExceptionMessage, expectation); 284 } 285 } 286