Skip to content

Commit 27bb8dd

Browse files
committed
Size matchers should provide insightful debugging information
Now, matchers like hasSize and similar, in case of mismatch, print also the actual values contained in the collection being tested. Closes #174
1 parent 3f08267 commit 27bb8dd

File tree

9 files changed

+136
-34
lines changed

9 files changed

+136
-34
lines changed

hamcrest/src/main/java/org/hamcrest/collection/IsArrayWithSize.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
package org.hamcrest.collection;
22

3-
import org.hamcrest.FeatureMatcher;
43
import org.hamcrest.Matcher;
54

5+
import java.util.Arrays;
6+
67
import static org.hamcrest.core.DescribedAs.describedAs;
78
import static org.hamcrest.core.IsEqual.equalTo;
89

910
/**
1011
* Matches if array size satisfies a nested matcher.
1112
*/
12-
public class IsArrayWithSize<E> extends FeatureMatcher<E[], Integer> {
13+
public class IsArrayWithSize<E> extends SizeMatcher<E[], E> {
1314
public IsArrayWithSize(Matcher<? super Integer> sizeMatcher) {
14-
super(sizeMatcher, "an array with size","array size");
15+
super(sizeMatcher, "array");
1516
}
1617

1718
@Override
1819
protected Integer featureValueOf(E[] actual) {
1920
return actual.length;
2021
}
2122

23+
@Override
24+
protected Iterable<? extends E> actualValues(E[] actual) {
25+
return Arrays.asList(actual);
26+
}
27+
2228
/**
2329
* Creates a matcher for arrays that matches when the <code>length</code> of the array
2430
* satisfies the specified matcher.

hamcrest/src/main/java/org/hamcrest/collection/IsCollectionWithSize.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.hamcrest.collection;
22

3-
import org.hamcrest.FeatureMatcher;
43
import org.hamcrest.Matcher;
54

65
import java.util.Collection;
@@ -10,16 +9,21 @@
109
/**
1110
* Matches if collection size satisfies a nested matcher.
1211
*/
13-
public class IsCollectionWithSize<E> extends FeatureMatcher<Collection<? extends E>, Integer> {
12+
public class IsCollectionWithSize<E> extends SizeMatcher<Collection<? extends E>, E> {
1413
public IsCollectionWithSize(Matcher<? super Integer> sizeMatcher) {
15-
super(sizeMatcher, "a collection with size", "collection size");
14+
super(sizeMatcher, "collection");
1615
}
1716

1817
@Override
1918
protected Integer featureValueOf(Collection<? extends E> actual) {
2019
return actual.size();
2120
}
2221

22+
@Override
23+
protected Iterable<? extends E> actualValues(Collection<? extends E> actual) {
24+
return actual;
25+
}
26+
2327
/**
2428
* Creates a matcher for {@link java.util.Collection}s that matches when the <code>size()</code> method returns
2529
* a value that satisfies the specified matcher.

hamcrest/src/main/java/org/hamcrest/collection/IsIterableWithSize.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package org.hamcrest.collection;
22

3-
import org.hamcrest.FeatureMatcher;
43
import org.hamcrest.Matcher;
54

65
import java.util.Iterator;
76

87
import static org.hamcrest.core.IsEqual.equalTo;
98

10-
public class IsIterableWithSize<E> extends FeatureMatcher<Iterable<E>, Integer> {
9+
public class IsIterableWithSize<E> extends SizeMatcher<Iterable<E>, E> {
1110

1211
public IsIterableWithSize(Matcher<? super Integer> sizeMatcher) {
13-
super(sizeMatcher, "an iterable with size", "iterable size");
12+
super(sizeMatcher, "iterable");
1413
}
15-
1614

1715
@Override
1816
protected Integer featureValueOf(Iterable<E> actual) {
@@ -23,6 +21,11 @@ protected Integer featureValueOf(Iterable<E> actual) {
2321
return size;
2422
}
2523

24+
@Override
25+
protected Iterable<? extends E> actualValues(Iterable<E> actual) {
26+
return actual;
27+
}
28+
2629
/**
2730
* Creates a matcher for {@link Iterable}s that matches when a single pass over the
2831
* examined {@link Iterable} yields an item count that satisfies the specified

hamcrest/src/main/java/org/hamcrest/collection/IsMapWithSize.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
package org.hamcrest.collection;
22

3-
import org.hamcrest.FeatureMatcher;
43
import org.hamcrest.Matcher;
54

65
import java.util.Map;
6+
import java.util.Set;
77

88
import static org.hamcrest.core.IsEqual.equalTo;
99

1010
/**
1111
* Matches if map size satisfies a nested matcher.
1212
*/
13-
public final class IsMapWithSize<K, V> extends FeatureMatcher<Map<? extends K, ? extends V>, Integer> {
13+
public final class IsMapWithSize<K, V> extends SizeMatcher<Map<? extends K, ? extends V>, Map.Entry<? extends K, ? extends V>> {
1414
@SuppressWarnings("WeakerAccess")
1515
public IsMapWithSize(Matcher<? super Integer> sizeMatcher) {
16-
super(sizeMatcher, "a map with size", "map size");
16+
super(sizeMatcher, "map");
1717
}
1818

1919
@Override
2020
protected Integer featureValueOf(Map<? extends K, ? extends V> actual) {
2121
return actual.size();
2222
}
2323

24+
@Override
25+
protected Set<? extends Map.Entry<? extends K, ? extends V>> actualValues(Map<? extends K, ? extends V> actual) {
26+
return actual.entrySet();
27+
}
28+
2429
/**
2530
* Creates a matcher for {@link java.util.Map}s that matches when the <code>size()</code> method returns
2631
* a value that satisfies the specified matcher.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.hamcrest.collection;
2+
3+
import org.hamcrest.Description;
4+
import org.hamcrest.FeatureMatcher;
5+
import org.hamcrest.Matcher;
6+
7+
/**
8+
* Base class for Matchers matching a feature on a collection-like object.
9+
* <p>
10+
* Provides insightful debugging information in case the collection does not match,
11+
* by printing the actual values in the mismatch description.
12+
*
13+
* @param <T> the type of the collection
14+
* @param <E> the type of the elements in the collection
15+
*/
16+
public abstract class SizeMatcher<T, E> extends FeatureMatcher<T, Integer> {
17+
18+
public SizeMatcher(Matcher<? super Integer> sizeMatcher, String type) {
19+
super(sizeMatcher, indefiniteArticle(type) + " " + type + " with size", type + " size");
20+
}
21+
22+
@Override
23+
protected abstract Integer featureValueOf(T actual);
24+
25+
@Override
26+
protected boolean matchesSafely(T actual, Description mismatch) {
27+
boolean matchesSafely = super.matchesSafely(actual, mismatch);
28+
if (!matchesSafely) {
29+
mismatch.appendText(". Actual values: ");
30+
mismatch.appendValueList("[", ", ", "]", actualValues(actual));
31+
}
32+
return matchesSafely;
33+
}
34+
35+
protected abstract Iterable<? extends E> actualValues(T actual);
36+
37+
private static String indefiniteArticle(String string) {
38+
// a naive algorithm..
39+
switch (string.charAt(0)) {
40+
case 'a':
41+
case 'e':
42+
case 'i':
43+
case 'o':
44+
case 'u':
45+
return "an";
46+
default:
47+
return "a";
48+
}
49+
}
50+
}

hamcrest/src/test/java/org/hamcrest/collection/IsArrayWithSizeTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,9 @@ public void testHasAReadableDescription() {
3333
assertDescription("an array with size <3>", arrayWithSize(equalTo(3)));
3434
assertDescription("an empty array", emptyArray());
3535
}
36+
37+
public void testOnMismatchProvidesInsightfulDebuggingInformation() {
38+
assertMismatchDescription("array size was <2>. Actual values: [<1>, <2>]",
39+
arrayWithSize(equalTo(1)), new Integer[] {1, 2});
40+
}
3641
}

hamcrest/src/test/java/org/hamcrest/collection/IsCollectionWithSizeTest.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,48 +21,48 @@ protected Matcher<?> createMatcher() {
2121

2222
public void testMatchesWhenSizeIsCorrect() {
2323
assertMatches("correct size", hasSize(equalTo(2)), asList(null, null));
24-
assertMismatchDescription("collection size was <3>", hasSize(equalTo(2)), asList(null, null, null));
24+
assertDoesNotMatch("incorrect size", hasSize(equalTo(2)), asList(null, null, null));
2525
}
2626

2727
public void testMatchesCollectionWhenSizeIsCorrectUsingObjectElementType() {
2828
Collection<Object> list = asList(null, null);
2929
assertMatches("correct size", hasSize(equalTo(2)), list);
30-
assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
30+
assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list);
3131
}
3232

3333
public void testMatchesCollectionWhenSizeIsCorrectUsingStringElementType() {
3434
Collection<String> list = asList("a", "b");
3535
assertMatches("correct size", hasSize(equalTo(2)), list);
36-
assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
36+
assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list);
3737
}
3838

3939
public void testMatchesCollectionWhenSizeIsCorrectUsingWildcardElementType() {
4040
Collection<?> list = asList("a", "b");
4141
assertMatches("correct size", hasSize(equalTo(2)), list);
42-
assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
42+
assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list);
4343
}
4444

4545
public void testMatchesListWhenSizeIsCorrectUsingObjectElementType() {
4646
List<Object> list = asList(null, null);
4747
assertMatches("correct size", hasSize(equalTo(2)), list);
48-
assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
48+
assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list);
4949
}
5050

5151
public void testMatchesListWhenSizeIsCorrectUsingStringElementType() {
5252
List<String> list = asList("a", "b");
5353
assertMatches("correct size", hasSize(equalTo(2)), list);
54-
assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
54+
assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list);
5555
}
5656

5757
public void testMatchesListWhenSizeIsCorrectUsingWildcardElementType() {
5858
List<?> list = asList("a", "b");
5959
assertMatches("correct size", hasSize(equalTo(2)), list);
60-
assertMismatchDescription("collection size was <2>", hasSize(equalTo(3)), list);
60+
assertDoesNotMatch("incorrect size", hasSize(equalTo(3)), list);
6161
}
6262

6363
public void testProvidesConvenientShortcutForHasSizeEqualTo() {
6464
assertMatches("correct size", hasSize(2), asList(null, null));
65-
assertMismatchDescription("collection size was <3>", hasSize(2), asList(null, null, null));
65+
assertDoesNotMatch("incorrect size", hasSize(2), asList(null, null, null));
6666
}
6767

6868
public void testHasAReadableDescription() {
@@ -74,4 +74,10 @@ public void testCompilesWithATypedCollection() {
7474
ArrayList<String> arrayList = new ArrayList<String>();
7575
MatcherAssert.assertThat(arrayList, hasSize(0));
7676
}
77+
78+
public void testOnMismatchProvidesInsightfulDebuggingInformation() {
79+
List<String> threeStrings = asList("a", "b", "c");
80+
assertMismatchDescription("collection size was <3>. Actual values: [\"a\", \"b\", \"c\"]",
81+
hasSize(equalTo(2)), threeStrings);
82+
}
7783
}

hamcrest/src/test/java/org/hamcrest/collection/IsIterableWithSizeTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.util.Arrays;
77
import java.util.Collections;
8+
import java.util.List;
89

910
import static org.hamcrest.collection.IsIterableWithSize.iterableWithSize;
1011

@@ -34,4 +35,9 @@ public void testDoesNotMatchIncorrectSize() throws Exception {
3435
public void testHasAReadableDescription() {
3536
assertDescription("an iterable with size <4>", iterableWithSize(4));
3637
}
38+
39+
public void testOnMismatchProvidesInsightfulDebuggingInformation() {
40+
assertMismatchDescription("iterable size was <2>. Actual values: [<1>, <2>]",
41+
iterableWithSize(1), Arrays.asList(1, 2));
42+
}
3743
}

hamcrest/src/test/java/org/hamcrest/collection/IsMapWithSizeTest.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
import org.hamcrest.MatcherAssert;
66

77
import java.util.HashMap;
8+
import java.util.Iterator;
9+
import java.util.List;
810
import java.util.Map;
911

12+
import static java.util.Arrays.asList;
1013
import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
1114
import static org.hamcrest.core.IsEqual.equalTo;
1215

@@ -19,48 +22,48 @@ protected Matcher<?> createMatcher() {
1922

2023
public void testMatchesWhenSizeIsCorrect() {
2124
assertMatches("correct size", aMapWithSize(equalTo(2)), mapWithKeys("a", "b"));
22-
assertMismatchDescription("map size was <3>", aMapWithSize(equalTo(2)), mapWithKeys("a", "b", "c"));
25+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(2)), mapWithKeys("a", "b", "c"));
2326
}
2427

2528
public void testMatchesMapWhenSizeIsCorrectUsingObjectElementType() {
2629
Map<Object, Object> map = mapWithKeys(new Object(), new Object());
2730
assertMatches("correct size", aMapWithSize(equalTo(2)), map);
28-
assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
31+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map);
2932
}
3033

3134
public void testMatchesMapWhenSizeIsCorrectUsingStringElementType() {
3235
Map<String, Integer> map = mapWithKeys("a", "b");
3336
assertMatches("correct size", aMapWithSize(equalTo(2)), map);
34-
assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
37+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map);
3538
}
3639

3740
public void testMatchesMapWhenSizeIsCorrectUsingWildcardElementType() {
3841
Map<?, ?> map = mapWithKeys("a", "b");
3942
assertMatches("correct size", aMapWithSize(equalTo(2)), map);
40-
assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
43+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map);
4144
}
4245

4346
public void testMatchesListWhenSizeIsCorrectUsingObjectElementType() {
4447
Map<Object, Object> map = mapWithKeys(new Object(), new Object());
4548
assertMatches("correct size", aMapWithSize(equalTo(2)), map);
46-
assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), map);
49+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map);
4750
}
4851

4952
public void testMatchesListWhenSizeIsCorrectUsingStringElementType() {
50-
Map<String, Integer> list = mapWithKeys("a", "b");
51-
assertMatches("correct size", aMapWithSize(equalTo(2)), list);
52-
assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), list);
53+
Map<String, Integer> map = mapWithKeys("a", "b");
54+
assertMatches("correct size", aMapWithSize(equalTo(2)), map);
55+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map);
5356
}
5457

5558
public void testMatchesListWhenSizeIsCorrectUsingWildcardElementType() {
56-
Map<?, ?> list = mapWithKeys("a", "b");
57-
assertMatches("correct size", aMapWithSize(equalTo(2)), list);
58-
assertMismatchDescription("map size was <2>", aMapWithSize(equalTo(3)), list);
59+
Map<?, ?> map = mapWithKeys("a", "b");
60+
assertMatches("correct size", aMapWithSize(equalTo(2)), map);
61+
assertDoesNotMatch("incorrect size", aMapWithSize(equalTo(3)), map);
5962
}
6063

6164
public void testProvidesConvenientShortcutForHasSizeEqualTo() {
6265
assertMatches("correct size", aMapWithSize(2), mapWithKeys(new Object(), new Object()));
63-
assertMismatchDescription("map size was <3>", aMapWithSize(2), mapWithKeys(new Object(), new Object(), new Object()));
66+
assertDoesNotMatch("incorrect size", aMapWithSize(2), mapWithKeys(new Object(), new Object(), new Object()));
6467
}
6568

6669
public void testHasAReadableDescription() {
@@ -71,12 +74,26 @@ public void testCompilesWithATypedMap() {
7174
Map<String, Integer> arrayList = new HashMap<String, Integer>();
7275
MatcherAssert.assertThat(arrayList, aMapWithSize(0));
7376
}
74-
77+
78+
public void testOnMismatchProvidesInsightfulDebuggingInformation() {
79+
assertMismatchDescription("map size was <2>. Actual values: [<k1=a>, <k2=b>]",
80+
aMapWithSize(equalTo(1)), mapWithKeysAndValues(asList("k1", "k2"), asList("a", "b")));
81+
}
82+
7583
private static <K, V> Map<K, V> mapWithKeys(K... keys) {
7684
final Map<K, V> result = new HashMap<K, V>();
7785
for (K key : keys) {
7886
result.put(key, null);
7987
}
8088
return result;
8189
}
90+
91+
private static <K, V> Map<K, V> mapWithKeysAndValues(List<K> keys, List<V> values) {
92+
final Map<K, V> result = new HashMap<K, V>();
93+
Iterator<V> valuesIt = values.iterator();
94+
for (K key : keys) {
95+
result.put(key, valuesIt.next());
96+
}
97+
return result;
98+
}
8299
}

0 commit comments

Comments
 (0)