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 }