package org.junit.rules; import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.matchers.JUnitMatchers.both; import static org.junit.matchers.JUnitMatchers.containsString; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.StringDescription; import org.junit.Assert; import org.junit.internal.matchers.TypeSafeMatcher; import org.junit.runners.model.Statement; /** * The ExpectedException Rule allows in-test specification of expected exception * types and messages: * *
 * // These tests all pass.
 * public static class HasExpectedException {
 * 	@Rule
 * 	public ExpectedException thrown= ExpectedException.none();
 * 
 * 	@Test
 * 	public void throwsNothing() {
 *    // no exception expected, none thrown: passes.
 * 	}
 * 
 * 	@Test
 * 	public void throwsNullPointerException() {
 * 		thrown.expect(NullPointerException.class);
 * 		throw new NullPointerException();
 * 	}
 * 
 * 	@Test
 * 	public void throwsNullPointerExceptionWithMessage() {
 * 		thrown.expect(NullPointerException.class);
 * 		thrown.expectMessage("happened?");
 * 		thrown.expectMessage(startsWith("What"));
 * 		throw new NullPointerException("What happened?");
 * 	}
 * }
 * 
*/ public class ExpectedException implements TestRule { /** * @return a Rule that expects no exception to be thrown * (identical to behavior without this Rule) */ public static ExpectedException none() { return new ExpectedException(); } private Matcher fMatcher= null; private ExpectedException() { } public Statement apply(Statement base, org.junit.runner.Description description) { return new ExpectedExceptionStatement(base); } /** * Adds {@code matcher} to the list of requirements for any thrown exception. */ // Should be able to remove this suppression in some brave new hamcrest world. @SuppressWarnings("unchecked") public void expect(Matcher matcher) { if (fMatcher == null) fMatcher= (Matcher) matcher; else fMatcher= both(fMatcher).and(matcher); } /** * Adds to the list of requirements for any thrown exception that it * should be an instance of {@code type} */ public void expect(Class type) { expect(instanceOf(type)); } /** * Adds to the list of requirements for any thrown exception that it * should contain string {@code substring} */ public void expectMessage(String substring) { expectMessage(containsString(substring)); } /** * Adds {@code matcher} to the list of requirements for the message * returned from any thrown exception. */ public void expectMessage(Matcher matcher) { expect(hasMessage(matcher)); } private class ExpectedExceptionStatement extends Statement { private final Statement fNext; public ExpectedExceptionStatement(Statement base) { fNext= base; } @Override public void evaluate() throws Throwable { try { fNext.evaluate(); } catch (Throwable e) { if (fMatcher == null) throw e; Assert.assertThat(e, fMatcher); return; } if (fMatcher != null) throw new AssertionError("Expected test to throw " + StringDescription.toString(fMatcher)); } } private Matcher hasMessage(final Matcher matcher) { return new TypeSafeMatcher() { public void describeTo(Description description) { description.appendText("exception with message "); description.appendDescriptionOf(matcher); } @Override public boolean matchesSafely(Throwable item) { return matcher.matches(item.getMessage()); } }; } }