aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2017-02-24 13:32:56 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-02-24 13:32:56 +0000
commit44deb443eba961ee519dbbfacfb919dcee95dda0 (patch)
tree9930cc8a22092529188aaa026b3af0681c8c3023
parentbbbc818f31a7553a6202e6063f079703f897356d (diff)
parentfacdecc8e055d722b8c0e0c8fbf8a8644e7fec57 (diff)
downloadjunit-44deb443eba961ee519dbbfacfb919dcee95dda0.tar.gz
Revert "Revert matchers back to 4.10 to compile against Hamcrest 1.1"
am: facdecc8e0 Change-Id: I66362f845bb8f67fa8bbd570a4cd06984e4053f1
-rw-r--r--README.version1
-rw-r--r--src/main/java/org/junit/Assume.java15
-rw-r--r--src/main/java/org/junit/internal/matchers/CombinableMatcher.java34
-rw-r--r--src/main/java/org/junit/internal/matchers/Each.java24
-rw-r--r--src/main/java/org/junit/internal/matchers/IsCollectionContaining.java67
-rw-r--r--src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java56
-rw-r--r--src/main/java/org/junit/internal/matchers/StringContains.java31
-rw-r--r--src/main/java/org/junit/internal/matchers/SubstringMatcher.java28
-rw-r--r--src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java50
-rw-r--r--src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java37
-rw-r--r--src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java21
-rw-r--r--src/main/java/org/junit/matchers/JUnitMatchers.java166
-rw-r--r--src/main/java/org/junit/rules/ExpectedException.java382
-rw-r--r--src/main/java/org/junit/rules/ExpectedExceptionMatcherBuilder.java46
14 files changed, 564 insertions, 394 deletions
diff --git a/README.version b/README.version
index 41e2c6f..fd9d421 100644
--- a/README.version
+++ b/README.version
@@ -5,5 +5,4 @@ BugComponent: 40416
Local Changes:
Remove DisableOnDebug (new in 4.12) as it is not supported on Android
Remove support for stuck threads
- Revert matchers back to 4.10 to compile against Hamcrest 1.1
Extra generic type information to aid certain javacs.
diff --git a/src/main/java/org/junit/Assume.java b/src/main/java/org/junit/Assume.java
index c96ff99..b7687f7 100644
--- a/src/main/java/org/junit/Assume.java
+++ b/src/main/java/org/junit/Assume.java
@@ -1,12 +1,12 @@
package org.junit;
import static java.util.Arrays.asList;
+import static org.hamcrest.CoreMatchers.everyItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import org.hamcrest.Matcher;
-import org.junit.internal.matchers.Each;
/**
* A set of methods useful for stating assumptions about the conditions in which a test is meaningful.
@@ -66,13 +66,12 @@ public class Assume {
assumeTrue(message, !b);
}
- /**
- * If called with one or more null elements in <code>objects</code>, the test will halt and be ignored.
- * @param objects
- */
- public static void assumeNotNull(Object... objects) {
- assumeThat(asList(objects), Each.each(notNullValue()));
- }
+ /**
+ * If called with one or more null elements in <code>objects</code>, the test will halt and be ignored.
+ */
+ public static void assumeNotNull(Object... objects) {
+ assumeThat(asList(objects), everyItem(notNullValue()));
+ }
/**
* Call to assume that <code>actual</code> satisfies the condition specified by <code>matcher</code>.
diff --git a/src/main/java/org/junit/internal/matchers/CombinableMatcher.java b/src/main/java/org/junit/internal/matchers/CombinableMatcher.java
deleted file mode 100644
index e9e6947..0000000
--- a/src/main/java/org/junit/internal/matchers/CombinableMatcher.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.junit.internal.matchers;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.anyOf;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-
-public class CombinableMatcher<T> extends BaseMatcher<T> {
-
- private final Matcher<? extends T> fMatcher;
-
- public CombinableMatcher(Matcher<? extends T> matcher) {
- fMatcher= matcher;
- }
-
- public boolean matches(Object item) {
- return fMatcher.matches(item);
- }
-
- public void describeTo(Description description) {
- description.appendDescriptionOf(fMatcher);
- }
-
- @SuppressWarnings("unchecked")
- public CombinableMatcher<T> and(Matcher<? extends T> matcher) {
- return new CombinableMatcher<T>(allOf(matcher, fMatcher));
- }
-
- @SuppressWarnings("unchecked")
- public CombinableMatcher<T> or(Matcher<? extends T> matcher) {
- return new CombinableMatcher<T>(anyOf(matcher, fMatcher));
- }
-} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/Each.java b/src/main/java/org/junit/internal/matchers/Each.java
deleted file mode 100644
index 527db3b..0000000
--- a/src/main/java/org/junit/internal/matchers/Each.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.junit.internal.matchers;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-
-public class Each {
- public static <T> Matcher<Iterable<T>> each(final Matcher<T> individual) {
- final Matcher<Iterable<T>> allItemsAre = not(hasItem(not(individual)));
-
- return new BaseMatcher<Iterable<T>>() {
- public boolean matches(Object item) {
- return allItemsAre.matches(item);
- }
-
- public void describeTo(Description description) {
- description.appendText("each ");
- individual.describeTo(description);
- }
- };
- }
-}
diff --git a/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java b/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java
deleted file mode 100644
index 4436a83..0000000
--- a/src/main/java/org/junit/internal/matchers/IsCollectionContaining.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.junit.internal.matchers;
-
-import static org.hamcrest.core.AllOf.allOf;
-import static org.hamcrest.core.IsEqual.equalTo;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import org.hamcrest.Description;
-import org.hamcrest.Factory;
-import org.hamcrest.Matcher;
-
-// Copied (hopefully temporarily) from hamcrest-library
-public class IsCollectionContaining<T> extends TypeSafeMatcher<Iterable<T>> {
- private final Matcher<? extends T> elementMatcher;
-
- public IsCollectionContaining(Matcher<? extends T> elementMatcher) {
- this.elementMatcher = elementMatcher;
- }
-
- @Override
- public boolean matchesSafely(Iterable<T> collection) {
- for (T item : collection) {
- if (elementMatcher.matches(item)){
- return true;
- }
- }
- return false;
- }
-
- public void describeTo(Description description) {
- description
- .appendText("a collection containing ")
- .appendDescriptionOf(elementMatcher);
- }
-
- @Factory
- public static <T> Matcher<Iterable<T>> hasItem(Matcher<? extends T> elementMatcher) {
- return new IsCollectionContaining<T>(elementMatcher);
- }
-
- @Factory
- public static <T> Matcher<Iterable<T>> hasItem(T element) {
- return hasItem(equalTo(element));
- }
-
- @Factory
- public static <T> Matcher<Iterable<T>> hasItems(Matcher<? extends T>... elementMatchers) {
- Collection<Matcher<? extends Iterable<T>>> all
- = new ArrayList<Matcher<? extends Iterable<T>>>(elementMatchers.length);
- for (Matcher<? extends T> elementMatcher : elementMatchers) {
- all.add(hasItem(elementMatcher));
- }
- return allOf(all);
- }
-
- @Factory
- public static <T> Matcher<Iterable<T>> hasItems(T... elements) {
- Collection<Matcher<? extends Iterable<T>>> all
- = new ArrayList<Matcher<? extends Iterable<T>>>(elements.length);
- for (T element : elements) {
- all.add(hasItem(element));
- }
- return allOf(all);
- }
-
-}
diff --git a/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
new file mode 100644
index 0000000..5d45ba3
--- /dev/null
+++ b/src/main/java/org/junit/internal/matchers/StacktracePrintingMatcher.java
@@ -0,0 +1,56 @@
+package org.junit.internal.matchers;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+
+/**
+ * A matcher that delegates to throwableMatcher and in addition appends the
+ * stacktrace of the actual Throwable in case of a mismatch.
+ */
+public class StacktracePrintingMatcher<T extends Throwable> extends
+ org.hamcrest.TypeSafeMatcher<T> {
+
+ private final Matcher<T> throwableMatcher;
+
+ public StacktracePrintingMatcher(Matcher<T> throwableMatcher) {
+ this.throwableMatcher = throwableMatcher;
+ }
+
+ public void describeTo(Description description) {
+ throwableMatcher.describeTo(description);
+ }
+
+ @Override
+ protected boolean matchesSafely(T item) {
+ return throwableMatcher.matches(item);
+ }
+
+ @Override
+ protected void describeMismatchSafely(T item, Description description) {
+ throwableMatcher.describeMismatch(item, description);
+ description.appendText("\nStacktrace was: ");
+ description.appendText(readStacktrace(item));
+ }
+
+ private String readStacktrace(Throwable throwable) {
+ StringWriter stringWriter = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(stringWriter));
+ return stringWriter.toString();
+ }
+
+ @Factory
+ public static <T extends Throwable> Matcher<T> isThrowable(
+ Matcher<T> throwableMatcher) {
+ return new StacktracePrintingMatcher<T>(throwableMatcher);
+ }
+
+ @Factory
+ public static <T extends Exception> Matcher<T> isException(
+ Matcher<T> exceptionMatcher) {
+ return new StacktracePrintingMatcher<T>(exceptionMatcher);
+ }
+}
diff --git a/src/main/java/org/junit/internal/matchers/StringContains.java b/src/main/java/org/junit/internal/matchers/StringContains.java
deleted file mode 100644
index e5f5334..0000000
--- a/src/main/java/org/junit/internal/matchers/StringContains.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2000-2006 hamcrest.org
- */
-package org.junit.internal.matchers;
-
-import org.hamcrest.Factory;
-import org.hamcrest.Matcher;
-
-/**
- * Tests if the argument is a string that contains a substring.
- */
-public class StringContains extends SubstringMatcher {
- public StringContains(String substring) {
- super(substring);
- }
-
- @Override
- protected boolean evalSubstringOf(String s) {
- return s.indexOf(substring) >= 0;
- }
-
- @Override
- protected String relationship() {
- return "containing";
- }
-
- @Factory
- public static Matcher<String> containsString(String substring) {
- return new StringContains(substring);
- }
-
-} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/SubstringMatcher.java b/src/main/java/org/junit/internal/matchers/SubstringMatcher.java
deleted file mode 100644
index 1c65240..0000000
--- a/src/main/java/org/junit/internal/matchers/SubstringMatcher.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.junit.internal.matchers;
-
-import org.hamcrest.Description;
-
-public abstract class SubstringMatcher extends TypeSafeMatcher<String> {
-
- protected final String substring;
-
- protected SubstringMatcher(final String substring) {
- this.substring = substring;
- }
-
- @Override
- public boolean matchesSafely(String item) {
- return evalSubstringOf(item);
- }
-
- public void describeTo(Description description) {
- description.appendText("a string ")
- .appendText(relationship())
- .appendText(" ")
- .appendValue(substring);
- }
-
- protected abstract boolean evalSubstringOf(String string);
-
- protected abstract String relationship();
-} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
new file mode 100644
index 0000000..22ce8bd
--- /dev/null
+++ b/src/main/java/org/junit/internal/matchers/ThrowableCauseMatcher.java
@@ -0,0 +1,50 @@
+package org.junit.internal.matchers;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * A matcher that applies a delegate matcher to the cause of the current Throwable, returning the result of that
+ * match.
+ *
+ * @param <T> the type of the throwable being matched
+ */
+public class ThrowableCauseMatcher<T extends Throwable> extends
+ TypeSafeMatcher<T> {
+
+ private final Matcher<? extends Throwable> causeMatcher;
+
+ public ThrowableCauseMatcher(Matcher<? extends Throwable> causeMatcher) {
+ this.causeMatcher = causeMatcher;
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("exception with cause ");
+ description.appendDescriptionOf(causeMatcher);
+ }
+
+ @Override
+ protected boolean matchesSafely(T item) {
+ return causeMatcher.matches(item.getCause());
+ }
+
+ @Override
+ protected void describeMismatchSafely(T item, Description description) {
+ description.appendText("cause ");
+ causeMatcher.describeMismatch(item.getCause(), description);
+ }
+
+ /**
+ * Returns a matcher that verifies that the outer exception has a cause for which the supplied matcher
+ * evaluates to true.
+ *
+ * @param matcher to apply to the cause of the outer exception
+ * @param <T> type of the outer exception
+ */
+ @Factory
+ public static <T extends Throwable> Matcher<T> hasCause(final Matcher<? extends Throwable> matcher) {
+ return new ThrowableCauseMatcher<T>(matcher);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java b/src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java
new file mode 100644
index 0000000..74386a8
--- /dev/null
+++ b/src/main/java/org/junit/internal/matchers/ThrowableMessageMatcher.java
@@ -0,0 +1,37 @@
+package org.junit.internal.matchers;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class ThrowableMessageMatcher<T extends Throwable> extends
+ TypeSafeMatcher<T> {
+
+ private final Matcher<String> matcher;
+
+ public ThrowableMessageMatcher(Matcher<String> matcher) {
+ this.matcher = matcher;
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("exception with message ");
+ description.appendDescriptionOf(matcher);
+ }
+
+ @Override
+ protected boolean matchesSafely(T item) {
+ return matcher.matches(item.getMessage());
+ }
+
+ @Override
+ protected void describeMismatchSafely(T item, Description description) {
+ description.appendText("message ");
+ matcher.describeMismatch(item.getMessage(), description);
+ }
+
+ @Factory
+ public static <T extends Throwable> Matcher<T> hasMessage(final Matcher<String> matcher) {
+ return new ThrowableMessageMatcher<T>(matcher);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
index 794a174..4e2cc12 100644
--- a/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
+++ b/src/main/java/org/junit/internal/matchers/TypeSafeMatcher.java
@@ -3,13 +3,16 @@ package org.junit.internal.matchers;
import java.lang.reflect.Method;
import org.hamcrest.BaseMatcher;
+import org.junit.internal.MethodSorter;
/**
* Convenient base class for Matchers that require a non-null value of a specific type.
* This simply implements the null check, checks the type and then casts.
*
* @author Joe Walnes
+ * @deprecated Please use {@link org.hamcrest.TypeSafeMatcher}.
*/
+@Deprecated
public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
private Class<?> expectedType;
@@ -23,27 +26,27 @@ public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
protected TypeSafeMatcher() {
expectedType = findExpectedType(getClass());
}
-
+
private static Class<?> findExpectedType(Class<?> fromClass) {
for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) {
- for (Method method : c.getDeclaredMethods()) {
+ for (Method method : MethodSorter.getDeclaredMethods(c)) {
if (isMatchesSafelyMethod(method)) {
return method.getParameterTypes()[0];
}
}
}
-
+
throw new Error("Cannot determine correct type for matchesSafely() method.");
}
-
+
private static boolean isMatchesSafelyMethod(Method method) {
- return method.getName().equals("matchesSafely")
- && method.getParameterTypes().length == 1
- && !method.isSynthetic();
+ return method.getName().equals("matchesSafely")
+ && method.getParameterTypes().length == 1
+ && !method.isSynthetic();
}
-
+
protected TypeSafeMatcher(Class<T> expectedType) {
- this.expectedType = expectedType;
+ this.expectedType = expectedType;
}
/**
diff --git a/src/main/java/org/junit/matchers/JUnitMatchers.java b/src/main/java/org/junit/matchers/JUnitMatchers.java
index 847a347..13407cc 100644
--- a/src/main/java/org/junit/matchers/JUnitMatchers.java
+++ b/src/main/java/org/junit/matchers/JUnitMatchers.java
@@ -1,83 +1,113 @@
package org.junit.matchers;
+import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;
-import org.junit.internal.matchers.CombinableMatcher;
-import org.junit.internal.matchers.Each;
-import org.junit.internal.matchers.IsCollectionContaining;
-import org.junit.internal.matchers.StringContains;
+import org.hamcrest.core.CombinableMatcher.CombinableBothMatcher;
+import org.hamcrest.core.CombinableMatcher.CombinableEitherMatcher;
+import org.junit.internal.matchers.StacktracePrintingMatcher;
/**
* Convenience import class: these are useful matchers for use with the assertThat method, but they are
* not currently included in the basic CoreMatchers class from hamcrest.
+ *
+ * @since 4.4
*/
public class JUnitMatchers {
- /**
- * @param element
- * @return A matcher matching any collection containing element
- */
- public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItem(T element) {
- return IsCollectionContaining.hasItem(element);
- }
+ /**
+ * @return A matcher matching any collection containing element
+ * @deprecated Please use {@link CoreMatchers#hasItem(Object)} instead.
+ */
+ @Deprecated
+ public static <T> Matcher<Iterable<? super T>> hasItem(T element) {
+ return CoreMatchers.hasItem(element);
+ }
- /**
- * @param elementMatcher
- * @return A matcher matching any collection containing an element matching elementMatcher
- */
- public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItem(org.hamcrest.Matcher<? extends T> elementMatcher) {
- return IsCollectionContaining.<T>hasItem(elementMatcher);
- }
+ /**
+ * @return A matcher matching any collection containing an element matching elementMatcher
+ * @deprecated Please use {@link CoreMatchers#hasItem(Matcher)} instead.
+ */
+ @Deprecated
+ public static <T> Matcher<Iterable<? super T>> hasItem(Matcher<? super T> elementMatcher) {
+ return CoreMatchers.<T>hasItem(elementMatcher);
+ }
- /**
- * @param elements
- * @return A matcher matching any collection containing every element in elements
- */
- public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(T... elements) {
- return IsCollectionContaining.hasItems(elements);
- }
+ /**
+ * @return A matcher matching any collection containing every element in elements
+ * @deprecated Please use {@link CoreMatchers#hasItems(Object...)} instead.
+ */
+ @Deprecated
+ public static <T> Matcher<Iterable<T>> hasItems(T... elements) {
+ return CoreMatchers.hasItems(elements);
+ }
- /**
- * @param elementMatchers
- * @return A matcher matching any collection containing at least one element that matches
- * each matcher in elementMatcher (this may be one element matching all matchers,
- * or different elements matching each matcher)
- */
- public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(org.hamcrest.Matcher<? extends T>... elementMatchers) {
- return IsCollectionContaining.<T>hasItems(elementMatchers);
- }
+ /**
+ * @return A matcher matching any collection containing at least one element that matches
+ * each matcher in elementMatcher (this may be one element matching all matchers,
+ * or different elements matching each matcher)
+ * @deprecated Please use {@link CoreMatchers#hasItems(Matcher...)} instead.
+ */
+ @Deprecated
+ public static <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... elementMatchers) {
+ return CoreMatchers.hasItems(elementMatchers);
+ }
- /**
- * @param elementMatcher
- * @return A matcher matching any collection in which every element matches elementMatcher
- */
- public static <T> Matcher<Iterable<T>> everyItem(final Matcher<T> elementMatcher) {
- return Each.each(elementMatcher);
- }
+ /**
+ * @return A matcher matching any collection in which every element matches elementMatcher
+ * @deprecated Please use {@link CoreMatchers#everyItem(Matcher)} instead.
+ */
+ @Deprecated
+ public static <T> Matcher<Iterable<T>> everyItem(final Matcher<T> elementMatcher) {
+ return CoreMatchers.everyItem(elementMatcher);
+ }
- /**
- * @param substring
- * @return a matcher matching any string that contains substring
- */
- public static org.hamcrest.Matcher<java.lang.String> containsString(java.lang.String substring) {
- return StringContains.containsString(substring);
- }
-
- /**
- * This is useful for fluently combining matchers that must both pass. For example:
- * <pre>
- * assertThat(string, both(containsString("a")).and(containsString("b")));
- * </pre>
- */
- public static <T> CombinableMatcher<T> both(Matcher<T> matcher) {
- return new CombinableMatcher<T>(matcher);
- }
-
- /**
- * This is useful for fluently combining matchers where either may pass, for example:
- * <pre>
- * assertThat(string, either(containsString("a")).or(containsString("b")));
- * </pre>
- */
- public static <T> CombinableMatcher<T> either(Matcher<T> matcher) {
- return new CombinableMatcher<T>(matcher);
- }
+ /**
+ * @return a matcher matching any string that contains substring
+ * @deprecated Please use {@link CoreMatchers#containsString(String)} instead.
+ */
+ @Deprecated
+ public static Matcher<java.lang.String> containsString(java.lang.String substring) {
+ return CoreMatchers.containsString(substring);
+ }
+
+ /**
+ * This is useful for fluently combining matchers that must both pass. For example:
+ * <pre>
+ * assertThat(string, both(containsString("a")).and(containsString("b")));
+ * </pre>
+ *
+ * @deprecated Please use {@link CoreMatchers#both(Matcher)} instead.
+ */
+ @Deprecated
+ public static <T> CombinableBothMatcher<T> both(Matcher<? super T> matcher) {
+ return CoreMatchers.both(matcher);
+ }
+
+ /**
+ * This is useful for fluently combining matchers where either may pass, for example:
+ * <pre>
+ * assertThat(string, either(containsString("a")).or(containsString("b")));
+ * </pre>
+ *
+ * @deprecated Please use {@link CoreMatchers#either(Matcher)} instead.
+ */
+ @Deprecated
+ public static <T> CombinableEitherMatcher<T> either(Matcher<? super T> matcher) {
+ return CoreMatchers.either(matcher);
+ }
+
+ /**
+ * @return A matcher that delegates to throwableMatcher and in addition
+ * appends the stacktrace of the actual Throwable in case of a mismatch.
+ */
+ public static <T extends Throwable> Matcher<T> isThrowable(Matcher<T> throwableMatcher) {
+ return StacktracePrintingMatcher.isThrowable(throwableMatcher);
+ }
+
+ /**
+ * @return A matcher that delegates to exceptionMatcher and in addition
+ * appends the stacktrace of the actual Exception in case of a mismatch.
+ */
+ public static <T extends Exception> Matcher<T> isException(Matcher<T> exceptionMatcher) {
+ return StacktracePrintingMatcher.isException(exceptionMatcher);
+ }
}
diff --git a/src/main/java/org/junit/rules/ExpectedException.java b/src/main/java/org/junit/rules/ExpectedException.java
index bac2fba..4d61712 100644
--- a/src/main/java/org/junit/rules/ExpectedException.java
+++ b/src/main/java/org/junit/rules/ExpectedException.java
@@ -1,136 +1,270 @@
package org.junit.rules;
+import static java.lang.String.format;
+import static org.hamcrest.CoreMatchers.containsString;
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 static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.internal.matchers.ThrowableCauseMatcher.hasCause;
+import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
+
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
-import org.junit.Assert;
-import org.junit.internal.matchers.TypeSafeMatcher;
+import org.junit.AssumptionViolatedException;
import org.junit.runners.model.Statement;
/**
- * The ExpectedException Rule allows in-test specification of expected exception
- * types and messages:
- *
- * <pre>
- * // These tests all pass.
- * public static class HasExpectedException {
- * &#064;Rule
- * public ExpectedException thrown= ExpectedException.none();
- *
- * &#064;Test
- * public void throwsNothing() {
- * // no exception expected, none thrown: passes.
- * }
+ * The {@code ExpectedException} rule allows you to verify that your code
+ * throws a specific exception.
+ *
+ * <h3>Usage</h3>
+ *
+ * <pre> public class SimpleExpectedExceptionTest {
+ * &#064;Rule
+ * public ExpectedException thrown= ExpectedException.none();
+ *
+ * &#064;Test
+ * public void throwsNothing() {
+ * // no exception expected, none thrown: passes.
+ * }
+ *
+ * &#064;Test
+ * public void throwsExceptionWithSpecificType() {
+ * thrown.expect(NullPointerException.class);
+ * throw new NullPointerException();
+ * }
+ * }</pre>
*
- * &#064;Test
- * public void throwsNullPointerException() {
- * thrown.expect(NullPointerException.class);
- * throw new NullPointerException();
- * }
- *
- * &#064;Test
- * public void throwsNullPointerExceptionWithMessage() {
- * thrown.expect(NullPointerException.class);
- * thrown.expectMessage(&quot;happened?&quot;);
- * thrown.expectMessage(startsWith(&quot;What&quot;));
- * throw new NullPointerException(&quot;What happened?&quot;);
- * }
- * }
- * </pre>
+ * <p>
+ * You have to add the {@code ExpectedException} rule to your test.
+ * This doesn't affect your existing tests (see {@code throwsNothing()}).
+ * After specifiying the type of the expected exception your test is
+ * successful when such an exception is thrown and it fails if a
+ * different or no exception is thrown.
+ *
+ * <p>
+ * Instead of specifying the exception's type you can characterize the
+ * expected exception based on other criterias, too:
+ *
+ * <ul>
+ * <li>The exception's message contains a specific text: {@link #expectMessage(String)}</li>
+ * <li>The exception's message complies with a Hamcrest matcher: {@link #expectMessage(Matcher)}</li>
+ * <li>The exception's cause complies with a Hamcrest matcher: {@link #expectCause(Matcher)}</li>
+ * <li>The exception itself complies with a Hamcrest matcher: {@link #expect(Matcher)}</li>
+ * </ul>
+ *
+ * <p>
+ * You can combine any of the presented expect-methods. The test is
+ * successful if all specifications are met.
+ * <pre> &#064;Test
+ * public void throwsException() {
+ * thrown.expect(NullPointerException.class);
+ * thrown.expectMessage(&quot;happened&quot;);
+ * throw new NullPointerException(&quot;What happened?&quot;);
+ * }</pre>
+ *
+ * <h3>AssumptionViolatedExceptions</h3>
+ * <p>
+ * JUnit uses {@link AssumptionViolatedException}s for indicating that a test
+ * provides no useful information. (See {@link org.junit.Assume} for more
+ * information.) You have to call {@code assume} methods before you set
+ * expectations of the {@code ExpectedException} rule. In this case the rule
+ * will not handle consume the exceptions and it can be handled by the
+ * framework. E.g. the following test is ignored by JUnit's default runner.
+ *
+ * <pre> &#064;Test
+ * public void ignoredBecauseOfFailedAssumption() {
+ * assumeTrue(false); // throws AssumptionViolatedException
+ * thrown.expect(NullPointerException.class);
+ * }</pre>
+ *
+ * <h3>AssertionErrors</h3>
+ *
+ * <p>
+ * JUnit uses {@link AssertionError}s for indicating that a test is failing. You
+ * have to call {@code assert} methods before you set expectations of the
+ * {@code ExpectedException} rule, if they should be handled by the framework.
+ * E.g. the following test fails because of the {@code assertTrue} statement.
+ *
+ * <pre> &#064;Test
+ * public void throwsUnhandled() {
+ * assertTrue(false); // throws AssertionError
+ * thrown.expect(NullPointerException.class);
+ * }</pre>
+ *
+ * <h3>Missing Exceptions</h3>
+ * <p>
+ * By default missing exceptions are reported with an error message
+ * like "Expected test to throw an instance of foo". You can configure a different
+ * message by means of {@link #reportMissingExceptionWithMessage(String)}. You
+ * can use a {@code %s} placeholder for the description of the expected
+ * exception. E.g. "Test doesn't throw %s." will fail with the error message
+ * "Test doesn't throw an instance of foo.".
+ *
+ * @since 4.7
*/
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<Object> 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<Object>) 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<? extends Throwable> type) {
- expect(instanceOf(type));
- }
-
- /**
- * Adds to the list of requirements for any thrown exception that it
- * should <em>contain</em> 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<String> 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<Throwable> hasMessage(final Matcher<String> matcher) {
- return new TypeSafeMatcher<Throwable>() {
- public void describeTo(Description description) {
- description.appendText("exception with message ");
- description.appendDescriptionOf(matcher);
- }
-
- @Override
- public boolean matchesSafely(Throwable item) {
- return matcher.matches(item.getMessage());
- }
- };
- }
+ /**
+ * Returns a {@linkplain TestRule rule} that expects no exception to
+ * be thrown (identical to behavior without this rule).
+ */
+ public static ExpectedException none() {
+ return new ExpectedException();
+ }
+
+ private final ExpectedExceptionMatcherBuilder matcherBuilder = new ExpectedExceptionMatcherBuilder();
+
+ private String missingExceptionMessage= "Expected test to throw %s";
+
+ private ExpectedException() {
+ }
+
+ /**
+ * This method does nothing. Don't use it.
+ * @deprecated AssertionErrors are handled by default since JUnit 4.12. Just
+ * like in JUnit &lt;= 4.10.
+ */
+ @Deprecated
+ public ExpectedException handleAssertionErrors() {
+ return this;
+ }
+
+ /**
+ * This method does nothing. Don't use it.
+ * @deprecated AssumptionViolatedExceptions are handled by default since
+ * JUnit 4.12. Just like in JUnit &lt;= 4.10.
+ */
+ @Deprecated
+ public ExpectedException handleAssumptionViolatedExceptions() {
+ return this;
+ }
+
+ /**
+ * Specifies the failure message for tests that are expected to throw
+ * an exception but do not throw any. You can use a {@code %s} placeholder for
+ * the description of the expected exception. E.g. "Test doesn't throw %s."
+ * will fail with the error message
+ * "Test doesn't throw an instance of foo.".
+ *
+ * @param message exception detail message
+ * @return the rule itself
+ */
+ public ExpectedException reportMissingExceptionWithMessage(String message) {
+ missingExceptionMessage = message;
+ return this;
+ }
+
+ public Statement apply(Statement base,
+ org.junit.runner.Description description) {
+ return new ExpectedExceptionStatement(base);
+ }
+
+ /**
+ * Verify that your code throws an exception that is matched by
+ * a Hamcrest matcher.
+ * <pre> &#064;Test
+ * public void throwsExceptionThatCompliesWithMatcher() {
+ * NullPointerException e = new NullPointerException();
+ * thrown.expect(is(e));
+ * throw e;
+ * }</pre>
+ */
+ public void expect(Matcher<?> matcher) {
+ matcherBuilder.add(matcher);
+ }
+
+ /**
+ * Verify that your code throws an exception that is an
+ * instance of specific {@code type}.
+ * <pre> &#064;Test
+ * public void throwsExceptionWithSpecificType() {
+ * thrown.expect(NullPointerException.class);
+ * throw new NullPointerException();
+ * }</pre>
+ */
+ public void expect(Class<? extends Throwable> type) {
+ expect(instanceOf(type));
+ }
+
+ /**
+ * Verify that your code throws an exception whose message contains
+ * a specific text.
+ * <pre> &#064;Test
+ * public void throwsExceptionWhoseMessageContainsSpecificText() {
+ * thrown.expectMessage(&quot;happened&quot;);
+ * throw new NullPointerException(&quot;What happened?&quot;);
+ * }</pre>
+ */
+ public void expectMessage(String substring) {
+ expectMessage(containsString(substring));
+ }
+
+ /**
+ * Verify that your code throws an exception whose message is matched
+ * by a Hamcrest matcher.
+ * <pre> &#064;Test
+ * public void throwsExceptionWhoseMessageCompliesWithMatcher() {
+ * thrown.expectMessage(startsWith(&quot;What&quot;));
+ * throw new NullPointerException(&quot;What happened?&quot;);
+ * }</pre>
+ */
+ public void expectMessage(Matcher<String> matcher) {
+ expect(hasMessage(matcher));
+ }
+
+ /**
+ * Verify that your code throws an exception whose cause is matched by
+ * a Hamcrest matcher.
+ * <pre> &#064;Test
+ * public void throwsExceptionWhoseCauseCompliesWithMatcher() {
+ * NullPointerException expectedCause = new NullPointerException();
+ * thrown.expectCause(is(expectedCause));
+ * throw new IllegalArgumentException(&quot;What happened?&quot;, cause);
+ * }</pre>
+ */
+ public void expectCause(Matcher<? extends Throwable> expectedCause) {
+ expect(hasCause(expectedCause));
+ }
+
+ private class ExpectedExceptionStatement extends Statement {
+ private final Statement next;
+
+ public ExpectedExceptionStatement(Statement base) {
+ next = base;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ next.evaluate();
+ } catch (Throwable e) {
+ handleException(e);
+ return;
+ }
+ if (isAnyExceptionExpected()) {
+ failDueToMissingException();
+ }
+ }
+ }
+
+ private void handleException(Throwable e) throws Throwable {
+ if (isAnyExceptionExpected()) {
+ assertThat(e, matcherBuilder.build());
+ } else {
+ throw e;
+ }
+ }
+
+ private boolean isAnyExceptionExpected() {
+ return matcherBuilder.expectsThrowable();
+ }
+
+ private void failDueToMissingException() throws AssertionError {
+ fail(missingExceptionMessage());
+ }
+
+ private String missingExceptionMessage() {
+ String expectation= StringDescription.toString(matcherBuilder.build());
+ return format(missingExceptionMessage, expectation);
+ }
}
diff --git a/src/main/java/org/junit/rules/ExpectedExceptionMatcherBuilder.java b/src/main/java/org/junit/rules/ExpectedExceptionMatcherBuilder.java
new file mode 100644
index 0000000..e7d94c4
--- /dev/null
+++ b/src/main/java/org/junit/rules/ExpectedExceptionMatcherBuilder.java
@@ -0,0 +1,46 @@
+package org.junit.rules;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.junit.matchers.JUnitMatchers.isThrowable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hamcrest.Matcher;
+
+/**
+ * Builds special matcher used by {@link ExpectedException}.
+ */
+class ExpectedExceptionMatcherBuilder {
+
+ private final List<Matcher<?>> matchers = new ArrayList<Matcher<?>>();
+
+ void add(Matcher<?> matcher) {
+ matchers.add(matcher);
+ }
+
+ boolean expectsThrowable() {
+ return !matchers.isEmpty();
+ }
+
+ Matcher<Throwable> build() {
+ return isThrowable(allOfTheMatchers());
+ }
+
+ private Matcher<Throwable> allOfTheMatchers() {
+ if (matchers.size() == 1) {
+ return cast(matchers.get(0));
+ }
+ return allOf(castedMatchers());
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private List<Matcher<? super Throwable>> castedMatchers() {
+ return new ArrayList<Matcher<? super Throwable>>((List) matchers);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Matcher<Throwable> cast(Matcher<?> singleMatcher) {
+ return (Matcher<Throwable>) singleMatcher;
+ }
+}