1 package org.junit.rules; 2 3 import static org.hamcrest.CoreMatchers.instanceOf; 4 import static org.junit.matchers.JUnitMatchers.both; 5 import static org.junit.matchers.JUnitMatchers.containsString; 6 import org.hamcrest.Description; 7 import org.hamcrest.Matcher; 8 import org.hamcrest.StringDescription; 9 import org.junit.Assert; 10 import org.junit.internal.matchers.TypeSafeMatcher; 11 import org.junit.runners.model.Statement; 12 13 /** 14 * The ExpectedException Rule allows in-test specification of expected exception 15 * types and messages: 16 * 17 * <pre> 18 * // These tests all pass. 19 * public static class HasExpectedException { 20 * @Rule 21 * public ExpectedException thrown= ExpectedException.none(); 22 * 23 * @Test 24 * public void throwsNothing() { 25 * // no exception expected, none thrown: passes. 26 * } 27 * 28 * @Test 29 * public void throwsNullPointerException() { 30 * thrown.expect(NullPointerException.class); 31 * throw new NullPointerException(); 32 * } 33 * 34 * @Test 35 * public void throwsNullPointerExceptionWithMessage() { 36 * thrown.expect(NullPointerException.class); 37 * thrown.expectMessage("happened?"); 38 * thrown.expectMessage(startsWith("What")); 39 * throw new NullPointerException("What happened?"); 40 * } 41 * } 42 * </pre> 43 */ 44 public class ExpectedException implements TestRule { 45 /** 46 * @return a Rule that expects no exception to be thrown 47 * (identical to behavior without this Rule) 48 */ 49 public static ExpectedException none() { 50 return new ExpectedException(); 51 } 52 53 private Matcher<Object> fMatcher= null; 54 55 private ExpectedException() { 56 57 } 58 59 public Statement apply(Statement base, 60 org.junit.runner.Description description) { 61 return new ExpectedExceptionStatement(base); 62 } 63 64 /** 65 * Adds {@code matcher} to the list of requirements for any thrown exception. 66 */ 67 // Should be able to remove this suppression in some brave new hamcrest world. 68 @SuppressWarnings("unchecked") 69 public void expect(Matcher<?> matcher) { 70 if (fMatcher == null) 71 fMatcher= (Matcher<Object>) matcher; 72 else 73 fMatcher= both(fMatcher).and(matcher); 74 } 75 76 /** 77 * Adds to the list of requirements for any thrown exception that it 78 * should be an instance of {@code type} 79 */ 80 public void expect(Class<? extends Throwable> type) { 81 expect(instanceOf(type)); 82 } 83 84 /** 85 * Adds to the list of requirements for any thrown exception that it 86 * should <em>contain</em> string {@code substring} 87 */ 88 public void expectMessage(String substring) { 89 expectMessage(containsString(substring)); 90 } 91 92 /** 93 * Adds {@code matcher} to the list of requirements for the message 94 * returned from any thrown exception. 95 */ 96 public void expectMessage(Matcher<String> matcher) { 97 expect(hasMessage(matcher)); 98 } 99 100 private class ExpectedExceptionStatement extends Statement { 101 private final Statement fNext; 102 103 public ExpectedExceptionStatement(Statement base) { 104 fNext= base; 105 } 106 107 @Override 108 public void evaluate() throws Throwable { 109 try { 110 fNext.evaluate(); 111 } catch (Throwable e) { 112 if (fMatcher == null) 113 throw e; 114 Assert.assertThat(e, fMatcher); 115 return; 116 } 117 if (fMatcher != null) 118 throw new AssertionError("Expected test to throw " 119 + StringDescription.toString(fMatcher)); 120 } 121 } 122 123 private Matcher<Throwable> hasMessage(final Matcher<String> matcher) { 124 return new TypeSafeMatcher<Throwable>() { 125 public void describeTo(Description description) { 126 description.appendText("exception with message "); 127 description.appendDescriptionOf(matcher); 128 } 129 130 @Override 131 public boolean matchesSafely(Throwable item) { 132 return matcher.matches(item.getMessage()); 133 } 134 }; 135 } 136 } 137