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 */ 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 <= 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 <= 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> @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> @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> @Test 193 * public void throwsExceptionWhoseMessageContainsSpecificText() { 194 * thrown.expectMessage("happened"); 195 * throw new NullPointerException("What happened?"); 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> @Test 206 * public void throwsExceptionWhoseMessageCompliesWithMatcher() { 207 * thrown.expectMessage(startsWith("What")); 208 * throw new NullPointerException("What happened?"); 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> @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 */ 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