2010-03-24 3 views
38

Ich möchte Hamcrest verwenden, um zu bestätigen, dass zwei Maps gleich sind, d. H. Sie haben die gleiche Menge von Schlüsseln, die auf die gleichen Werte zeigen.Map-Gleichheit mit Hamcrest

Meine aktuelle beste Vermutung ist:

assertThat(affA.entrySet(), hasItems(affB.entrySet()); 

die gibt:

Verfahren assertThat (T, Matcher) in der Art Assert nicht anwendbar für die Argumente (Set>, Matcher> >>)

Ich habe auch in Varianten von containsAll, und einige andere von den Hamcrest-Pakete zur Verfügung gestellt. Kann mir jemand in die richtige Richtung zeigen? Oder muss ich einen benutzerdefinierten Matcher schreiben?

+0

Ich habe auch versucht 'containsAll' et al. Vor einiger Zeit und es schien nicht zu funktionieren - anscheinend ist Hamcrest ein bisschen unzuverlässig noch :-( –

+7

Gibt es einen Grund, warum Sie die '.equals()' der Map-Implementierung nicht verwenden können? –

+3

Ah - ich hadn 'realisierte, dass Sammlungen richtige .equals() - Vergleiche machen. War das schon immer so? Das macht das Leben viel einfacher! Danke! –

Antwort

41

Der kürzeste Weg, ich habe kommen mit zwei Aussagen:

assertThat(affA.entrySet(), everyItem(isIn(affB.entrySet()))); 
assertThat(affB.entrySet(), everyItem(isIn(affA.entrySet()))); 

Aber man kann wohl auch tun:

assertThat(affA.entrySet(), equalTo(affB.entrySet())); 

in Abhängigkeit von den Implementierungen der Karten.

UPDATE: tatsächlich gibt es eine Anweisung, die unabhängig von den Sammlungstypen arbeitet:

assertThat(affA.entrySet, both(everyItem(isIn(affB.entrySet()))).and(containsInAnyOrder(affB.entrySet()))); 
+0

Was ist falsch mit .equals()? – Taras

+3

@Taras: die Berichterstattung ist weniger hilfreich mit' .equals() ' –

33

Manchmal Map.equals() ist genug. Aber manchmal wissen Sie nicht, die Typen von Map s wird von Code unter Tests zurückgegeben, so dass Sie nicht wissen, ob .equals() wird die Karte des unbekannten Typs ordnungsgemäß von Code mit Karte von Ihnen erstellt vergleichen. Oder Sie möchten Ihren Code nicht mit solchen Tests binden.

Zusätzlich separat eine Karte Konstruktion damit das Ergebnis zu vergleichen ist IMHO nicht sehr elegant:

Map<MyKey, MyValue> actual = methodUnderTest(); 

Map<MyKey, MyValue> expected = new HashMap<MyKey, MyValue>(); 
expected.put(new MyKey(1), new MyValue(10)); 
expected.put(new MyKey(2), new MyValue(20)); 
expected.put(new MyKey(3), new MyValue(30)); 
assertThat(actual, equalTo(expected)); 

ich lieber mit Macher:

import static org.hamcrest.Matchers.hasEntry; 

Map<MyKey, MyValue> actual = methodUnderTest(); 
assertThat(actual, allOf(
         hasSize(3), // make sure there are no extra key/value pairs in map 
         hasEntry(new MyKey(1), new MyValue(10)), 
         hasEntry(new MyKey(2), new MyValue(20)), 
         hasEntry(new MyKey(3), new MyValue(30)) 
)); 

ich hasSize() selbst definieren muß:

public static <K, V> Matcher<Map<K, V>> hasSize(final int size) { 
    return new TypeSafeMatcher<Map<K, V>>() { 
     @Override 
     public boolean matchesSafely(Map<K, V> kvMap) { 
      return kvMap.size() == size; 
     } 

     @Override 
     public void describeTo(Description description) { 
      description.appendText(" has ").appendValue(size).appendText(" key/value pairs"); 
     } 
    }; 
} 

Und es gibt eine andere Variante von hasEntry(), die Matcher als Parameter anstelle der exakten Werte von Schlüssel und Wert verwendet. Dies kann nützlich sein, wenn Sie für jeden Schlüssel und Wert etwas anderes als die Gleichheitsprüfung benötigen.

+2

Anstelle der eigenen' hasSize'-Methode sollten Sie 'org.hamcrest.collection verwenden .IsMapWithSize.aMapWithSize (Matcher ) ' – Eric

2

ich Guava ImmutableMap favorisieren verwenden. Sie unterstützen Map.equals() und sind einfach zu konstruieren. Der einzige Trick besteht darin, Typparameter explizit anzugeben, da hamcrest den Typ ImmutableMap annimmt.

assertThat(actualValue, 
      Matchers.<Map<String, String>>equalTo(ImmutableMap.of(
       "key1", "value", 
       "key2", "other-value" 
))); 
2

Eine weitere Option ist nun die Cirneco extension für hamcrest zu verwenden. Es hat hasSameKeySet() (sowie andere Matcher für Guava "Sammlungen"). Nach Ihrem Beispiel wird es sein:

assertThat(affA, hasSameKeySet(affB)); 

Sie können die folgende Abhängigkeit für ein JDK7-basiertes Projekt verwenden:

<dependency> 
    <groupId>it.ozimov</groupId> 
    <artifactId>java7-hamcrest-matchers</artifactId> 
    <version>0.7.0</version> 
</dependency> 

oder folgenden, wenn Sie mit JDK8 oder überlegen:

+0

Würde das nicht nur die Schlüssel vergleichen, aber nicht die Werte? Die Frage wollte auch Werte vergleichen –

+0

@ Dr.Hans-PeterStörr Nein, die Frage zeigt, dass die Eintragsmengen verglichen werden – JeanValjean

0

Hamcrest hat jetzt eine Matcher für Größensammlung.

org.hamcrest.collection.IsCollectionWithSize

+2

Das beantwortet die ursprüngliche Frage nicht. – Guenther

0

Dies wirkt wie ein Zauber und erfordert keine zwei Behauptungen wie die akzeptierte Antwort.

assertThat(actualData.entrySet().toArray(), 
    arrayContainingInAnyOrder(expectedData.entrySet().toArray())); 
0

Wenn Sie eine Reihe von Ergebnissen mit Erwartungen zu vergleichen, und wenn Sie assertj Bibliothek verwenden möchten, können Sie dies tun:

// put set of expected values by your test keys 
Map<K, V> expectations = ...; 

// for each test key get result 
Map<K, V> results = expectations.keySet().stream().collect(toMap(k -> k, k -> getYourProductionResult(k))); 

assertThat(results).containsAllEntriesOf(expectations); 

Beachten Sie, dass containsAllEntriesOf nicht vergleichen Karten für die Gleichstellung. Wenn Ihr Produktionscode tatsächlich eine Map<K, V> zurückgibt, können Sie eine Überprüfung für Schlüssel hinzufügen assertThat(results).containsOnlyKeys((K[]) expectations.keySet().toArray());