Home | History | Annotate | Download | only in rules
      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  * 	&#064;Rule
     21  * 	public ExpectedException thrown= ExpectedException.none();
     22  *
     23  * 	&#064;Test
     24  * 	public void throwsNothing() {
     25  *    // no exception expected, none thrown: passes.
     26  * 	}
     27  *
     28  * 	&#064;Test
     29  * 	public void throwsNullPointerException() {
     30  * 		thrown.expect(NullPointerException.class);
     31  * 		throw new NullPointerException();
     32  * 	}
     33  *
     34  * 	&#064;Test
     35  * 	public void throwsNullPointerExceptionWithMessage() {
     36  * 		thrown.expect(NullPointerException.class);
     37  * 		thrown.expectMessage(&quot;happened?&quot;);
     38  * 		thrown.expectMessage(startsWith(&quot;What&quot;));
     39  * 		throw new NullPointerException(&quot;What happened?&quot;);
     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