From 27a4ff845c9f6cdc660948d6fbc835185fb9bb21 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Sat, 31 Oct 2015 21:56:04 +0000 Subject: [PATCH 1/3] Matcher that allows the user to match the entries of a map --- .../src/main/java/org/hamcrest/Matchers.java | 13 +++++++ .../hamcrest/collection/IsMapWithEntries.java | 33 ++++++++++++++++++ .../collection/IsMapWithEntriesTest.java | 34 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java create mode 100644 hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java diff --git a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java b/hamcrest-library/src/main/java/org/hamcrest/Matchers.java index 34c750eb..3a0c8d0b 100644 --- a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java +++ b/hamcrest-library/src/main/java/org/hamcrest/Matchers.java @@ -1112,6 +1112,19 @@ public static org.hamcrest.Matcher return org.hamcrest.collection.IsMapContaining.hasEntry(key, value); } + /** + * Creates a matcher for {@link java.util.Map}s matching when the examined {@link java.util.Map}'s set of entries + * satisfies the specified entriesMatcher. + * For example: + *
assertThat(myMap, hasEntries(hasSize(2)))
+ * + * @param entriesMatcher + * the matcher that must be satisfied by the set of entries + */ + public static org.hamcrest.Matcher> hasEntries(Matcher>> entriesMatcher) { + return org.hamcrest.collection.IsMapWithEntries.hasEntries(entriesMatcher); + } + /** * Creates a matcher for {@link java.util.Map}s matching when the examined {@link java.util.Map} contains * at least one key that satisfies the specified matcher. diff --git a/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java b/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java new file mode 100644 index 00000000..3ec96ce5 --- /dev/null +++ b/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java @@ -0,0 +1,33 @@ +package org.hamcrest.collection; + +import org.hamcrest.FeatureMatcher; +import org.hamcrest.Matcher; + +import java.util.Map; +import java.util.Set; + +public class IsMapWithEntries extends FeatureMatcher, Set>> { + + public IsMapWithEntries(Matcher>> entriesMatcher) { + super(entriesMatcher, "a map with entries", "map entries"); + } + + @Override + protected Set> featureValueOf(Map actual) { + return actual.entrySet(); + } + + /** + * Creates a matcher for {@link Map}s matching when the examined {@link Map}'s set of entries + * satisfies the specified entriesMatcher. + * For example: + *
assertThat(myMap, hasEntries(hasSize(2)))
+ * + * @param entriesMatcher + * the matcher that must be satisfied by the set of entries + */ + public static Matcher> hasEntries(Matcher>> entriesMatcher) { + return new IsMapWithEntries<>(entriesMatcher); + } + +} diff --git a/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java b/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java new file mode 100644 index 00000000..705bc807 --- /dev/null +++ b/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java @@ -0,0 +1,34 @@ +package org.hamcrest.collection; + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; + +import java.util.Collections; + +import static org.hamcrest.collection.IsMapWithEntries.hasEntries; + +public class IsMapWithEntriesTest extends AbstractMatcherTest { + + @Override + protected Matcher createMatcher() { + return hasEntries(Matchers.empty()); + } + + public void testDoesNotMatchNull() { + assertMismatchDescription("was null", hasEntries(Matchers.empty()), null); + } + + public void testDoesNotMatchAMapWhoseEntriesDoNotSatisfyTheEntriesMatcher() { + assertMismatchDescription("map entries collection size was <0>", hasEntries(Matchers.hasSize(1)), Collections.emptyMap()); + } + + public void testMatchesAMapWhoseEntriesSatisfyTheEntriesMatcher() { + assertMatches(hasEntries(Matchers.hasSize(1)), Collections.singletonMap("k", "v")); + } + + public void testHasReadableDescription() { + assertDescription("a map with entries an empty collection", hasEntries(Matchers.empty())); + } + +} From 572d9196fe1b45b10975ee4d5cf0076a37d6ab3c Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Sat, 31 Oct 2015 23:14:06 +0000 Subject: [PATCH 2/3] Matcher that allows the user to match individual map entries --- .../src/main/java/org/hamcrest/Matchers.java | 56 +++++++++++ .../org/hamcrest/collection/IsMapEntry.java | 98 +++++++++++++++++++ .../hamcrest/collection/IsMapEntryTest.java | 61 ++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 hamcrest-library/src/main/java/org/hamcrest/collection/IsMapEntry.java create mode 100644 hamcrest-library/src/test/java/org/hamcrest/collection/IsMapEntryTest.java diff --git a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java b/hamcrest-library/src/main/java/org/hamcrest/Matchers.java index 3a0c8d0b..3e2067f2 100644 --- a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java +++ b/hamcrest-library/src/main/java/org/hamcrest/Matchers.java @@ -1125,6 +1125,62 @@ public static org.hamcrest.Matcher return org.hamcrest.collection.IsMapWithEntries.hasEntries(entriesMatcher); } + /** + * Creates a matcher for {@link java.util.Map.Entry}s matching when the examined {@link java.util.Map.Entry} has a key which satisfies + * the specified keyMatcher, and a value which satisfies the specified valueMatcher. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry(equalTo("key"), notNullValue())))
+ * + * @param keyMatcher + * the matcher that must be satisfied by the key + * @param valueMatcher + * the matcher that must be satisfied by the value + */ + public static Matcher> entry(Matcher keyMatcher, Matcher valueMatcher) { + return org.hamcrest.collection.IsMapEntry.entry(keyMatcher, valueMatcher); + } + + /** + * Creates a matcher for {@link java.util.Map.Entry}s matching when the examined {@link java.util.Map.Entry} has a key which satisfies + * the specified keyMatcher; the value is ignored. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry(equalTo("key"))))
+ * + * @param keyMatcher + * the matcher that must be satisfied by the key + */ + public static Matcher> entry(Matcher keyMatcher) { + return org.hamcrest.collection.IsMapEntry.entry(keyMatcher); + } + + /** + * Creates a matcher for {@link java.util.Map.Entry}s matching when the examined {@link java.util.Map.Entry} has the specified + * key, and a value which satisfies the specified valueMatcher. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry("key", notNullValue())))
+ * + * @param key + * the required key + * @param valueMatcher + * the matcher that must be satisfied by the value + */ + public static Matcher> entry(K key, Matcher valueMatcher) { + return org.hamcrest.collection.IsMapEntry.entry(key, valueMatcher); + } + + /** + * Creates a matcher for {@link java.util.Map.Entry}s matching when the examined {@link java.util.Map.Entry} has the specified + * key; the value is ignored. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry("key")))
+ * + * @param key + * the required key + */ + public static Matcher> entry(K key) { + return org.hamcrest.collection.IsMapEntry.entry(key); + } + /** * Creates a matcher for {@link java.util.Map}s matching when the examined {@link java.util.Map} contains * at least one key that satisfies the specified matcher. diff --git a/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapEntry.java b/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapEntry.java new file mode 100644 index 00000000..a6a8f9ce --- /dev/null +++ b/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapEntry.java @@ -0,0 +1,98 @@ +package org.hamcrest.collection; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.util.Map; + +import static java.util.Map.Entry; + +public class IsMapEntry extends TypeSafeDiagnosingMatcher> { + + private final Matcher keyMatcher; + private final Matcher valueMatcher; + + public IsMapEntry(Matcher keyMatcher, Matcher valueMatcher) { + this.keyMatcher = keyMatcher; + this.valueMatcher = valueMatcher; + } + + @Override + protected boolean matchesSafely(Map.Entry item, Description mismatchDescription) { + boolean matches = true; + + if (!keyMatcher.matches(item.getKey())) { + matches = false; + mismatchDescription.appendText("key "); + keyMatcher.describeMismatch(item.getKey(), mismatchDescription); + } + + if (valueMatcher != null && !valueMatcher.matches(item.getValue())) { + if (!matches) mismatchDescription.appendText(" and "); + matches = false; + mismatchDescription.appendText("value "); + valueMatcher.describeMismatch(item.getValue(), mismatchDescription); + } + + return matches; + } + + @Override + public void describeTo(Description description) { + description.appendText("an entry with key ").appendDescriptionOf(keyMatcher); + if (valueMatcher != null) description.appendText(" and value ").appendDescriptionOf(valueMatcher); + } + + /** + * Creates a matcher for {@link Entry}s matching when the examined {@link Entry} has a key which satisfies + * the specified keyMatcher, and a value which satisfies the specified valueMatcher. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry(equalTo("key"), notNullValue())))
+ * + * @param keyMatcher the matcher that must be satisfied by the key + * @param valueMatcher the matcher that must be satisfied by the value + */ + public static Matcher> entry(Matcher keyMatcher, Matcher valueMatcher) { + return new IsMapEntry<>(keyMatcher, valueMatcher); + } + + /** + * Creates a matcher for {@link Entry}s matching when the examined {@link Entry} has a key which satisfies + * the specified keyMatcher; the value is ignored. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry(equalTo("key"))))
+ * + * @param keyMatcher the matcher that must be satisfied by the key + */ + public static Matcher> entry(Matcher keyMatcher) { + return new IsMapEntry<>(keyMatcher, null); + } + + /** + * Creates a matcher for {@link Entry}s matching when the examined {@link Entry} has the specified + * key, and a value which satisfies the specified valueMatcher. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry("key", notNullValue())))
+ * + * @param key the required key + * @param valueMatcher the matcher that must be satisfied by the value + */ + public static Matcher> entry(K key, Matcher valueMatcher) { + return new IsMapEntry<>(Matchers.equalTo(key), valueMatcher); + } + + /** + * Creates a matcher for {@link Entry}s matching when the examined {@link Entry} has the specified + * key; the value is ignored. + * For example: + *
assertThat(myMap.keySet(), hasItem(entry("key")))
+ * + * @param key the required key + */ + public static Matcher> entry(K key) { + return new IsMapEntry<>(Matchers.equalTo(key), null); + } + +} diff --git a/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapEntryTest.java b/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapEntryTest.java new file mode 100644 index 00000000..39be6d89 --- /dev/null +++ b/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapEntryTest.java @@ -0,0 +1,61 @@ +package org.hamcrest.collection; + +import org.hamcrest.AbstractMatcherTest; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; + +import java.util.AbstractMap; +import java.util.Map; + +import static org.hamcrest.collection.IsMapEntry.entry; + +public class IsMapEntryTest extends AbstractMatcherTest { + + @Override + protected Matcher createMatcher() { + return entry(Matchers.equalTo("key"), Matchers.equalTo(23)); + } + + public void testDoesNotMatchNull() { + assertMismatchDescription("was null", entry(Matchers.equalTo(23), Matchers.equalTo("key")), null); + } + + public void testDoesNotMatchAnEntryWithTheWrongKey() { + assertMismatchDescription("key was \"jey\"", entry(Matchers.equalTo("key"), Matchers.equalTo(23)), new AbstractMap.SimpleEntry<>("jey", 23)); + } + + public void testDoesNotMatchAnEntryWithTheWrongValue() { + assertMismatchDescription("value was <24>", entry(Matchers.equalTo("key"), Matchers.equalTo(23)), new AbstractMap.SimpleEntry<>("key", 24)); + } + + public void testDoesNotMatchAnEntryWithTheWrongKeyAndValue() { + assertMismatchDescription("key was \"jey\" and value was <24>", entry(Matchers.equalTo("key"), Matchers.equalTo(23)), new AbstractMap.SimpleEntry<>("jey", 24)); + } + + public void testMatchesAnEntryWithTheRightKeyAndValue() { + assertMatches(entry(Matchers.equalTo("key"), Matchers.equalTo(23)), new AbstractMap.SimpleEntry<>("key", 23)); + } + + public void testHasReadableDescription() { + assertDescription("an entry with key \"key\" and value a value greater than <22>", entry(Matchers.equalTo("key"), Matchers.greaterThan(22))); + } + + public void testCanCreateWithLiteralKey() { + Matcher> matcher = IsMapEntry.entry("key", Matchers.greaterThan(22)); + assertMatches(matcher, new AbstractMap.SimpleEntry<>("key", 23)); + assertDescription("an entry with key \"key\" and value a value greater than <22>", matcher); + } + + public void testCanCreateWithKeyOnly() { + Matcher> matcher = IsMapEntry.entry(Matchers.equalTo("key")); + assertMatches(matcher, new AbstractMap.SimpleEntry<>("key", 99)); + assertDescription("an entry with key \"key\"", matcher); + } + + public void testCanCreateWithLiteralKeyOnly() { + Matcher> matcher = IsMapEntry.entry("key"); + assertMatches(matcher, new AbstractMap.SimpleEntry<>("key", 99)); + assertDescription("an entry with key \"key\"", matcher); + } + +} From 636f15221417309e7690ed2f6add8e63f6d7eb7c Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Sat, 31 Oct 2015 23:33:15 +0000 Subject: [PATCH 3/3] Augment the matcher that allows the user to match the entries of a map with a convenience factory method which takes a variable number of matchers for individual map entries --- .../src/main/java/org/hamcrest/Matchers.java | 14 ++++++++++++++ .../org/hamcrest/collection/IsMapWithEntries.java | 14 ++++++++++++++ .../hamcrest/collection/IsMapWithEntriesTest.java | 12 ++++++++++++ 3 files changed, 40 insertions(+) diff --git a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java b/hamcrest-library/src/main/java/org/hamcrest/Matchers.java index 3e2067f2..6585345a 100644 --- a/hamcrest-library/src/main/java/org/hamcrest/Matchers.java +++ b/hamcrest-library/src/main/java/org/hamcrest/Matchers.java @@ -1125,6 +1125,20 @@ public static org.hamcrest.Matcher return org.hamcrest.collection.IsMapWithEntries.hasEntries(entriesMatcher); } + /** + * Creates a matcher for {@link java.util.Map}s matching when the examined {@link java.util.Map}'s set of entries + * contains, in any order, entries satisfying the specified entriesMatchers. + * For example: + *
assertThat(myMap, hasEntries(entry("a key"), entry("another key")))
+ * + * @param entriesMatchers + * the matchers that must be satisfied by the entries + */ + @SafeVarargs + public static Matcher> hasEntries(Matcher>... entriesMatchers) { + return org.hamcrest.collection.IsMapWithEntries.hasEntries(entriesMatchers); + } + /** * Creates a matcher for {@link java.util.Map.Entry}s matching when the examined {@link java.util.Map.Entry} has a key which satisfies * the specified keyMatcher, and a value which satisfies the specified valueMatcher. diff --git a/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java b/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java index 3ec96ce5..4c32f1f1 100644 --- a/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java +++ b/hamcrest-library/src/main/java/org/hamcrest/collection/IsMapWithEntries.java @@ -30,4 +30,18 @@ public IsMapWithEntries(Matcher(entriesMatcher); } + /** + * Creates a matcher for {@link Map}s matching when the examined {@link Map}'s set of entries + * contains, in any order, entries satisfying the specified entriesMatchers. + * For example: + *
assertThat(myMap, hasEntries(entry("a key"), entry("another key")))
+ * + * @param entriesMatchers + * the matchers that must be satisfied by the entries + */ + @SafeVarargs + public static Matcher> hasEntries(Matcher>... entriesMatchers) { + return new IsMapWithEntries<>(IsIterableContainingInAnyOrder.containsInAnyOrder(entriesMatchers)); + } + } diff --git a/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java b/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java index 705bc807..3bab8e4b 100644 --- a/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java +++ b/hamcrest-library/src/test/java/org/hamcrest/collection/IsMapWithEntriesTest.java @@ -5,7 +5,10 @@ import org.hamcrest.Matchers; import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import static org.hamcrest.collection.IsMapEntry.entry; import static org.hamcrest.collection.IsMapWithEntries.hasEntries; public class IsMapWithEntriesTest extends AbstractMatcherTest { @@ -31,4 +34,13 @@ public void testHasReadableDescription() { assertDescription("a map with entries an empty collection", hasEntries(Matchers.empty())); } + public void testMatchesANumberOfExplicitEntriesInAnyOrder() { + Map map = new LinkedHashMap<>(); + map.put("c", 3); + map.put("b", 2); + map.put("a", 1); + + assertMatches(hasEntries(entry("a", Matchers.equalTo(1)), entry("b", Matchers.equalTo(2)), entry("c", Matchers.equalTo(3))), map); + } + }