diff --git a/hamcrest/src/main/java/org/hamcrest/collection/IsArrayWithSize.java b/hamcrest/src/main/java/org/hamcrest/collection/IsArrayWithSize.java index 4ae7235e..614afce2 100644 --- a/hamcrest/src/main/java/org/hamcrest/collection/IsArrayWithSize.java +++ b/hamcrest/src/main/java/org/hamcrest/collection/IsArrayWithSize.java @@ -1,17 +1,18 @@ package org.hamcrest.collection; -import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; +import java.util.Arrays; + import static org.hamcrest.core.DescribedAs.describedAs; import static org.hamcrest.core.IsEqual.equalTo; /** * Matches if array size satisfies a nested matcher. */ -public class IsArrayWithSize extends FeatureMatcher { +public class IsArrayWithSize extends SizeMatcher { public IsArrayWithSize(Matcher sizeMatcher) { - super(sizeMatcher, "an array with size","array size"); + super(sizeMatcher, "array"); } @Override @@ -19,6 +20,11 @@ protected Integer featureValueOf(E[] actual) { return actual.length; } + @Override + protected Iterable actualValues(E[] actual) { + return Arrays.asList(actual); + } + /** * Creates a matcher for arrays that matches when the length of the array * satisfies the specified matcher. diff --git a/hamcrest/src/main/java/org/hamcrest/collection/IsCollectionWithSize.java b/hamcrest/src/main/java/org/hamcrest/collection/IsCollectionWithSize.java index f79afdc6..1511517e 100644 --- a/hamcrest/src/main/java/org/hamcrest/collection/IsCollectionWithSize.java +++ b/hamcrest/src/main/java/org/hamcrest/collection/IsCollectionWithSize.java @@ -1,6 +1,5 @@ package org.hamcrest.collection; -import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; import java.util.Collection; @@ -10,9 +9,9 @@ /** * Matches if collection size satisfies a nested matcher. */ -public class IsCollectionWithSize extends FeatureMatcher, Integer> { +public class IsCollectionWithSize extends SizeMatcher, E> { public IsCollectionWithSize(Matcher sizeMatcher) { - super(sizeMatcher, "a collection with size", "collection size"); + super(sizeMatcher, "collection"); } @Override @@ -20,6 +19,11 @@ protected Integer featureValueOf(Collection actual) { return actual.size(); } + @Override + protected Iterable actualValues(Collection actual) { + return actual; + } + /** * Creates a matcher for {@link java.util.Collection}s that matches when the size() method returns * a value that satisfies the specified matcher. diff --git a/hamcrest/src/main/java/org/hamcrest/collection/IsIterableWithSize.java b/hamcrest/src/main/java/org/hamcrest/collection/IsIterableWithSize.java index 0a1535fe..824e065d 100644 --- a/hamcrest/src/main/java/org/hamcrest/collection/IsIterableWithSize.java +++ b/hamcrest/src/main/java/org/hamcrest/collection/IsIterableWithSize.java @@ -1,18 +1,16 @@ package org.hamcrest.collection; -import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; import java.util.Iterator; import static org.hamcrest.core.IsEqual.equalTo; -public class IsIterableWithSize extends FeatureMatcher, Integer> { +public class IsIterableWithSize extends SizeMatcher, E> { public IsIterableWithSize(Matcher sizeMatcher) { - super(sizeMatcher, "an iterable with size", "iterable size"); + super(sizeMatcher, "iterable"); } - @Override protected Integer featureValueOf(Iterable actual) { @@ -23,6 +21,11 @@ protected Integer featureValueOf(Iterable actual) { return size; } + @Override + protected Iterable actualValues(Iterable actual) { + return actual; + } + /** * Creates a matcher for {@link Iterable}s that matches when a single pass over the * examined {@link Iterable} yields an item count that satisfies the specified diff --git a/hamcrest/src/main/java/org/hamcrest/collection/IsMapWithSize.java b/hamcrest/src/main/java/org/hamcrest/collection/IsMapWithSize.java index 966d9ca2..505afd67 100644 --- a/hamcrest/src/main/java/org/hamcrest/collection/IsMapWithSize.java +++ b/hamcrest/src/main/java/org/hamcrest/collection/IsMapWithSize.java @@ -1,19 +1,19 @@ package org.hamcrest.collection; -import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; import java.util.Map; +import java.util.Set; import static org.hamcrest.core.IsEqual.equalTo; /** * Matches if map size satisfies a nested matcher. */ -public final class IsMapWithSize extends FeatureMatcher, Integer> { +public final class IsMapWithSize extends SizeMatcher, Map.Entry> { @SuppressWarnings("WeakerAccess") public IsMapWithSize(Matcher sizeMatcher) { - super(sizeMatcher, "a map with size", "map size"); + super(sizeMatcher, "map"); } @Override @@ -21,6 +21,11 @@ protected Integer featureValueOf(Map actual) { return actual.size(); } + @Override + protected Set> actualValues(Map actual) { + return actual.entrySet(); + } + /** * Creates a matcher for {@link java.util.Map}s that matches when the size() method returns * a value that satisfies the specified matcher. diff --git a/hamcrest/src/main/java/org/hamcrest/collection/SizeMatcher.java b/hamcrest/src/main/java/org/hamcrest/collection/SizeMatcher.java new file mode 100644 index 00000000..9d667b04 --- /dev/null +++ b/hamcrest/src/main/java/org/hamcrest/collection/SizeMatcher.java @@ -0,0 +1,50 @@ +package org.hamcrest.collection; + +import org.hamcrest.Description; +import org.hamcrest.FeatureMatcher; +import org.hamcrest.Matcher; + +/** + * Base class for Matchers matching a feature on a collection-like object. + *

+ * Provides insightful debugging information in case the collection does not match, + * by printing the actual values in the mismatch description. + * + * @param the type of the collection + * @param the type of the elements in the collection + */ +public abstract class SizeMatcher extends FeatureMatcher { + + public SizeMatcher(Matcher sizeMatcher, String type) { + super(sizeMatcher, indefiniteArticle(type) + " " + type + " with size", type + " size"); + } + + @Override + protected abstract Integer featureValueOf(T actual); + + @Override + protected boolean matchesSafely(T actual, Description mismatch) { + boolean matchesSafely = super.matchesSafely(actual, mismatch); + if (!matchesSafely) { + mismatch.appendText(". Actual values: "); + mismatch.appendValueList("[", ", ", "]", actualValues(actual)); + } + return matchesSafely; + } + + protected abstract Iterable actualValues(T actual); + + private static String indefiniteArticle(String string) { + // a naive algorithm.. + switch (string.charAt(0)) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return "an"; + default: + return "a"; + } + } +} diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java index 18f607ec..33c0bbaa 100644 --- a/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java +++ b/hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java @@ -33,4 +33,9 @@ public void testHasAReadableDescription() { assertDescription("an array with size <3>", arrayWithSize(equalTo(3))); assertDescription("an empty array", emptyArray()); } + + public void testOnMismatchProvidesInsightfulDebuggingInformation() { + assertMismatchDescription("array size was <2>. Actual values: [<1>, <2>]", + arrayWithSize(equalTo(1)), new Integer[] {1, 2}); + } } diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java index b04a0c5c..966a12c4 100644 --- a/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java +++ b/hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java @@ -21,48 +21,48 @@ protected Matcher createMatcher() { public void testMatchesWhenSizeIsCorrect() { assertMatches("correct size", hasSize(equalTo(2)), asList(null, null)); - assertMismatchDescription("collection size was <3>", hasSize(equalTo(2)), asList(null, null, null)); + assertDoesNotMatch("incorrect size", hasSize(equalTo(2)), asList(null, null, null)); } public void testMatchesCollectionWhenSizeIsCorrectUsingObjectElementType() { Collection list = asList(null, null); assertMatches("correct size", hasSize(equalTo(2)), list); - assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list); + assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list); } public void testMatchesCollectionWhenSizeIsCorrectUsingStringElementType() { Collection list = asList("a", "b"); assertMatches("correct size", hasSize(equalTo(2)), list); - assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list); + assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list); } public void testMatchesCollectionWhenSizeIsCorrectUsingWildcardElementType() { Collection list = asList("a", "b"); assertMatches("correct size", hasSize(equalTo(2)), list); - assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list); + assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list); } public void testMatchesListWhenSizeIsCorrectUsingObjectElementType() { List list = asList(null, null); assertMatches("correct size", hasSize(equalTo(2)), list); - assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list); + assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list); } public void testMatchesListWhenSizeIsCorrectUsingStringElementType() { List list = asList("a", "b"); assertMatches("correct size", hasSize(equalTo(2)), list); - assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list); + assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list); } public void testMatchesListWhenSizeIsCorrectUsingWildcardElementType() { List list = asList("a", "b"); assertMatches("correct size", hasSize(equalTo(2)), list); - assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list); + assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list); } public void testProvidesConvenientShortcutForHasSizeEqualTo() { assertMatches("correct size", hasSize(2), asList(null, null)); - assertMismatchDescription("collection size was <3>", hasSize(2), asList(null, null, null)); + assertDoesNotMatch("incorrect size", hasSize(2), asList(null, null, null)); } public void testHasAReadableDescription() { @@ -74,4 +74,10 @@ public void testCompilesWithATypedCollection() { ArrayList arrayList = new ArrayList(); MatcherAssert.assertThat(arrayList, hasSize(0)); } + + public void testOnMismatchProvidesInsightfulDebuggingInformation() { + List threeStrings = asList("a", "b", "c"); + assertMismatchDescription("collection size was <3>. Actual values: [\"a\", \"b\", \"c\"]", + hasSize(equalTo(2)), threeStrings); + } } diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java index 8bf65d14..66ecba50 100644 --- a/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java +++ b/hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.List; import static org.hamcrest.collection.IsIterableWithSize.iterableWithSize; @@ -34,4 +35,9 @@ public void testDoesNotMatchIncorrectSize() throws Exception { public void testHasAReadableDescription() { assertDescription("an iterable with size <4>", iterableWithSize(4)); } + + public void testOnMismatchProvidesInsightfulDebuggingInformation() { + assertMismatchDescription("iterable size was <2>. Actual values: [<1>, <2>]", + iterableWithSize(1), Arrays.asList(1, 2)); + } } diff --git a/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java b/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java index c050924c..d76dbe0f 100644 --- a/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java +++ b/hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java @@ -5,8 +5,11 @@ import org.hamcrest.MatcherAssert; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import static java.util.Arrays.asList; import static org.hamcrest.collection.IsMapWithSize.aMapWithSize; import static org.hamcrest.core.IsEqual.equalTo; @@ -19,48 +22,48 @@ protected Matcher createMatcher() { public void testMatchesWhenSizeIsCorrect() { assertMatches("correct size", aMapWithSize(equalTo(2)), mapWithKeys("a", "b")); - assertMismatchDescription("map size was <3>", aMapWithSize(equalTo(2)), mapWithKeys("a", "b", "c")); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(2)), mapWithKeys("a", "b", "c")); } public void testMatchesMapWhenSizeIsCorrectUsingObjectElementType() { Map map = mapWithKeys(new Object(), new Object()); assertMatches("correct size", aMapWithSize(equalTo(2)), map); - assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map); } public void testMatchesMapWhenSizeIsCorrectUsingStringElementType() { Map map = mapWithKeys("a", "b"); assertMatches("correct size", aMapWithSize(equalTo(2)), map); - assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map); } public void testMatchesMapWhenSizeIsCorrectUsingWildcardElementType() { Map map = mapWithKeys("a", "b"); assertMatches("correct size", aMapWithSize(equalTo(2)), map); - assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map); } public void testMatchesListWhenSizeIsCorrectUsingObjectElementType() { Map map = mapWithKeys(new Object(), new Object()); assertMatches("correct size", aMapWithSize(equalTo(2)), map); - assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map); } public void testMatchesListWhenSizeIsCorrectUsingStringElementType() { - Map list = mapWithKeys("a", "b"); - assertMatches("correct size", aMapWithSize(equalTo(2)), list); - assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), list); + Map map = mapWithKeys("a", "b"); + assertMatches("correct size", aMapWithSize(equalTo(2)), map); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map); } public void testMatchesListWhenSizeIsCorrectUsingWildcardElementType() { - Map list = mapWithKeys("a", "b"); - assertMatches("correct size", aMapWithSize(equalTo(2)), list); - assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), list); + Map map = mapWithKeys("a", "b"); + assertMatches("correct size", aMapWithSize(equalTo(2)), map); + assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map); } public void testProvidesConvenientShortcutForHasSizeEqualTo() { assertMatches("correct size", aMapWithSize(2), mapWithKeys(new Object(), new Object())); - assertMismatchDescription("map size was <3>", aMapWithSize(2), mapWithKeys(new Object(), new Object(), new Object())); + assertDoesNotMatch("incorrect size", aMapWithSize(2), mapWithKeys(new Object(), new Object(), new Object())); } public void testHasAReadableDescription() { @@ -71,7 +74,12 @@ public void testCompilesWithATypedMap() { Map arrayList = new HashMap(); MatcherAssert.assertThat(arrayList, aMapWithSize(0)); } - + + public void testOnMismatchProvidesInsightfulDebuggingInformation() { + assertMismatchDescription("map size was <2>. Actual values: [, ]", + aMapWithSize(equalTo(1)), mapWithKeysAndValues(asList("k1", "k2"), asList("a", "b"))); + } + private static Map mapWithKeys(K... keys) { final Map result = new HashMap(); for (K key : keys) { @@ -79,4 +87,13 @@ private static Map mapWithKeys(K... keys) { } return result; } + + private static Map mapWithKeysAndValues(List keys, List values) { + final Map result = new HashMap(); + Iterator valuesIt = values.iterator(); + for (K key : keys) { + result.put(key, valuesIt.next()); + } + return result; + } }