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 import org.hamcrest.Matcher; 011 import org.hamcrest.StringDescription; 012 import org.junit.AssumptionViolatedException; 013 import org.junit.runners.model.Statement; 014 015 /** 016 * The {@code ExpectedException} rule allows you to verify that your code 017 * throws a specific exception. 018 * 019 * <h3>Usage</h3> 020 * 021 * <pre> public class SimpleExpectedExceptionTest { 022 * @Rule 023 * public ExpectedException thrown = ExpectedException.none(); 024 * 025 * @Test 026 * public void throwsNothing() { 027 * // no exception expected, none thrown: passes. 028 * } 029 * 030 * @Test 031 * public void throwsExceptionWithSpecificType() { 032 * thrown.expect(NullPointerException.class); 033 * throw new NullPointerException(); 034 * } 035 * }</pre> 036 * 037 * <p>You have to add the {@code ExpectedException} rule to your test. 038 * This doesn't affect your existing tests (see {@code throwsNothing()}). 039 * After specifying the type of the expected exception your test is 040 * successful when such an exception is thrown and it fails if a 041 * different or no exception is thrown. 042 * 043 * <p>This rule does not perform any special magic to make execution continue 044 * as if the exception had not been thrown. So it is nearly always a mistake 045 * for a test method to have statements after the one that is expected to 046 * throw the exception. 047 * 048 * <p>Instead of specifying the exception's type you can characterize the 049 * expected exception based on other criteria, too: 050 * 051 * <ul> 052 * <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li> 053 * <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li> 054 * <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li> 055 * <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li> 056 * </ul> 057 * 058 * <p>You can combine any of the presented expect-methods. The test is 059 * successful if all specifications are met. 060 * <pre> @Test 061 * public void throwsException() { 062 * thrown.expect(NullPointerException.class); 063 * thrown.expectMessage("happened"); 064 * throw new NullPointerException("What happened?"); 065 * }</pre> 066 * 067 * <p>It is recommended to set the {@link org.junit.Rule#order() order} of the 068 * {@code ExpectedException} to {@code Integer.MAX_VALUE} if it is used together 069 * with another rule that handles exceptions, e.g. {@link ErrorCollector}. 070 * Otherwise failing tests may be successful. 071 * <pre> @Rule(order = Integer.MAX_VALUE) 072 * public ExpectedException thrown = ExpectedException.none();</pre> 073 * 074 * <h3>AssumptionViolatedExceptions</h3> 075 * <p>JUnit uses {@link AssumptionViolatedException}s for indicating that a test 076 * provides no useful information. (See {@link org.junit.Assume} for more 077 * information.) You have to call {@code assume} methods before you set 078 * expectations of the {@code ExpectedException} rule. In this case the rule 079 * will not handle consume the exceptions and it can be handled by the 080 * framework. E.g. the following test is ignored by JUnit's default runner. 081 * 082 * <pre> @Test 083 * public void ignoredBecauseOfFailedAssumption() { 084 * assumeTrue(false); // throws AssumptionViolatedException 085 * thrown.expect(NullPointerException.class); 086 * }</pre> 087 * 088 * <h3>AssertionErrors</h3> 089 * 090 * <p>JUnit uses {@link AssertionError}s for indicating that a test is failing. You 091 * have to call {@code assert} methods before you set expectations of the 092 * {@code ExpectedException} rule, if they should be handled by the framework. 093 * E.g. the following test fails because of the {@code assertTrue} statement. 094 * 095 * <pre> @Test 096 * public void throwsUnhandled() { 097 * assertTrue(false); // throws AssertionError 098 * thrown.expect(NullPointerException.class); 099 * }</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 * @deprecated Since 4.13 110 * {@link org.junit.Assert#assertThrows(Class, org.junit.function.ThrowingRunnable) 111 * Assert.assertThrows} can be used to verify that your code throws a specific 112 * exception. 113 * @since 4.7 114 */ 115 public class ExpectedException implements TestRule { 116 /** 117 * Returns a {@linkplain TestRule rule} that expects no exception to 118 * be thrown (identical to behavior without this rule). 119 */ 120 public static ExpectedException none() { 121 return new ExpectedException(); 122 } 123 124 private final ExpectedExceptionMatcherBuilder matcherBuilder = new ExpectedExceptionMatcherBuilder(); 125 126 private String missingExceptionMessage= "Expected test to throw %s"; 127 128 private ExpectedException() { 129 } 130 131 /** 132 * This method does nothing. Don't use it. 133 * @deprecated AssertionErrors are handled by default since JUnit 4.12. Just 134 * like in JUnit <= 4.10. 135 */ 136 @Deprecated 137 public ExpectedException handleAssertionErrors() { 138 return this; 139 } 140 141 /** 142 * This method does nothing. Don't use it. 143 * @deprecated AssumptionViolatedExceptions are handled by default since 144 * JUnit 4.12. Just like in JUnit <= 4.10. 145 */ 146 @Deprecated 147 public ExpectedException handleAssumptionViolatedExceptions() { 148 return this; 149 } 150 151 /** 152 * Specifies the failure message for tests that are expected to throw 153 * an exception but do not throw any. You can use a {@code %s} placeholder for 154 * the description of the expected exception. E.g. "Test doesn't throw %s." 155 * will fail with the error message 156 * "Test doesn't throw an instance of foo.". 157 * 158 * @param message exception detail message 159 * @return the rule itself 160 */ 161 public ExpectedException reportMissingExceptionWithMessage(String message) { 162 missingExceptionMessage = message; 163 return this; 164 } 165 166 public Statement apply(Statement base, 167 org.junit.runner.Description description) { 168 return new ExpectedExceptionStatement(base); 169 } 170 171 /** 172 * Verify that your code throws an exception that is matched by 173 * a Hamcrest matcher. 174 * <pre> @Test 175 * public void throwsExceptionThatCompliesWithMatcher() { 176 * NullPointerException e = new NullPointerException(); 177 * thrown.expect(is(e)); 178 * throw e; 179 * }</pre> 180 * 181 * @deprecated use {@code org.hamcrest.junit.ExpectedException.expect()} 182 */ 183 @Deprecated 184 public void expect(Matcher<?> matcher) { 185 matcherBuilder.add(matcher); 186 } 187 188 /** 189 * Verify that your code throws an exception that is an 190 * instance of specific {@code type}. 191 * <pre> @Test 192 * public void throwsExceptionWithSpecificType() { 193 * thrown.expect(NullPointerException.class); 194 * throw new NullPointerException(); 195 * }</pre> 196 */ 197 public void expect(Class<? extends Throwable> type) { 198 expect(instanceOf(type)); 199 } 200 201 /** 202 * Verify that your code throws an exception whose message contains 203 * a specific text. 204 * <pre> @Test 205 * public void throwsExceptionWhoseMessageContainsSpecificText() { 206 * thrown.expectMessage("happened"); 207 * throw new NullPointerException("What happened?"); 208 * }</pre> 209 */ 210 public void expectMessage(String substring) { 211 expectMessage(containsString(substring)); 212 } 213 214 /** 215 * Verify that your code throws an exception whose message is matched 216 * by a Hamcrest matcher. 217 * <pre> @Test 218 * public void throwsExceptionWhoseMessageCompliesWithMatcher() { 219 * thrown.expectMessage(startsWith("What")); 220 * throw new NullPointerException("What happened?"); 221 * }</pre> 222 * 223 * @deprecated use {@code org.hamcrest.junit.ExpectedException.expectMessage()} 224 */ 225 @Deprecated 226 public void expectMessage(Matcher<String> matcher) { 227 expect(hasMessage(matcher)); 228 } 229 230 /** 231 * Verify that your code throws an exception whose cause is matched by 232 * a Hamcrest matcher. 233 * <pre> @Test 234 * public void throwsExceptionWhoseCauseCompliesWithMatcher() { 235 * NullPointerException expectedCause = new NullPointerException(); 236 * thrown.expectCause(is(expectedCause)); 237 * throw new IllegalArgumentException("What happened?", cause); 238 * }</pre> 239 * 240 * @deprecated use {@code org.hamcrest.junit.ExpectedException.expectCause()} 241 */ 242 @Deprecated 243 public void expectCause(Matcher<?> expectedCause) { 244 expect(hasCause(expectedCause)); 245 } 246 247 /** 248 * Check if any Exception is expected. 249 * @since 4.13 250 */ 251 public final boolean isAnyExceptionExpected() { 252 return matcherBuilder.expectsThrowable(); 253 } 254 255 private class ExpectedExceptionStatement extends Statement { 256 private final Statement next; 257 258 public ExpectedExceptionStatement(Statement base) { 259 next = base; 260 } 261 262 @Override 263 public void evaluate() throws Throwable { 264 try { 265 next.evaluate(); 266 } catch (Throwable e) { 267 handleException(e); 268 return; 269 } 270 if (isAnyExceptionExpected()) { 271 failDueToMissingException(); 272 } 273 } 274 } 275 276 private void handleException(Throwable e) throws Throwable { 277 if (isAnyExceptionExpected()) { 278 assertThat(e, matcherBuilder.build()); 279 } else { 280 throw e; 281 } 282 } 283 284 private void failDueToMissingException() throws AssertionError { 285 fail(missingExceptionMessage()); 286 } 287 288 private String missingExceptionMessage() { 289 String expectation= StringDescription.toString(matcherBuilder.build()); 290 return format(missingExceptionMessage, expectation); 291 } 292 }