2010-03-24 12 views
38

Mi piacerebbe utilizzare hamcrest per affermare che due mappe sono uguali, cioè hanno lo stesso set di chiavi che puntano agli stessi valori.Uguaglianza mappa con Hamcrest

Il mio attuale ipotesi migliore è:

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

che dà:

Il metodo assertThat (T, Matcher) nel tipo Assert non è applicabile per gli argomenti (Set>, Matcher> >>)

Ho anche esaminato le varianti di containsAll e alcuni altri forniti dai pacchetti hamcrest. Qualcuno può indicarmi la giusta direzione? O devo scrivere un matcher personalizzato?

+0

Ho anche provato 'containsAll' et al. qualche tempo fa e non sembrava funzionare - apparentemente hamcrest è un po 'inaffidabile ancora :-( –

+7

C'è un motivo per cui non si può usare il '.equals()' dell'implementazione della mappa? –

+3

Ah - I hadn Mi sono reso conto che le raccolte fanno i confronti .equals() adeguati. È sempre stato così? Questo rende la vita molto più facile! Grazie! –

risposta

41

La via più breve che è venuta in mente è di due affermazioni:

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

Ma probabilmente si può anche fare:

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

a seconda delle implementazioni delle mappe.

UPDATE: in realtà c'è una dichiarazione che funziona indipendentemente dei tipi di raccolta:

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

Cosa c'è di sbagliato con .equals()? – Taras

+3

@Taras: la segnalazione è meno utile con' .equals() ' –

33

A volte è sufficiente Map.equals(). Ma a volte non sai che i tipi di Map vengono restituiti dal codice sotto test, quindi non sai se .equals() confronterà correttamente quella mappa di tipo sconosciuto restituita dal codice con la mappa costruita da te. O non vuoi legare il tuo codice con questi test.

Inoltre, la costruzione di una mappa a parte per confrontare il risultato con esso è IMHO non è molto elegante:

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)); 

Io preferisco usare machers:

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)) 
)); 

devo definire hasSize() me stesso:

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"); 
     } 
    }; 
} 

E c'è un'altra variante di hasEntry() che accetta i parametri di corrispondenza come parametri anziché i valori esatti di chiave e valore. Questo può essere utile nel caso in cui abbiate bisogno di qualcosa di diverso dal test di uguaglianza di ogni chiave e valore.

+2

Invece di usare il proprio metodo 'hasSize', dovresti usare,' org.hamcrest.collection .IsMapWithSize.aMapWithSize (Matcher ) ' – Eric

2

ho favore utilizzando Guava ImmutableMap. Supportano Map.equals() e sono facili da costruire. L'unico trucco è specificare esplicitamente i parametri del tipo, poiché hamcrest assumerà il tipo ImmutableMap.

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

Un'altra opzione disponibile ora è quello di utilizzare il Cirneco extension per Hamcrest. Ha lo hasSameKeySet() (così come gli altri abbinamenti per le "raccolte" di Guava). Secondo il vostro esempio, sarà:

assertThat(affA, hasSameKeySet(affB)); 

È possibile utilizzare il seguente dipendenza per un progetto JDK7-based:

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

o il seguente se si utilizza JDK8 o superiore:

<dependency> 
    <groupId>it.ozimov</groupId> 
    <artifactId>java8-hamcrest-matchers</artifactId> 
    <version>0.7.0</version> 
</dependency> 
+0

Non sarebbe sufficiente confrontare le chiavi ma non i valori? La domanda voleva comparare anche i valori. –

+0

@ Dr.Hans-PeterStörr No, la domanda mostra che i set di voci sono confrontati – JeanValjean

0

Hamcrest ora ha un Matcher per la raccolta delle dimensioni.

org.hamcrest.collection.IsCollectionWithSize

+2

Questo non risponde alla domanda originale. – Guenther

0

Questo funziona come un fascino e non richiede due affermazioni come la risposta accettata.

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

Se è necessario confrontare una serie di risultati con le aspettative e se si sceglie di utilizzare assertj biblioteca, si può fare questo:

// 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); 

noti che containsAllEntriesOf non confronta le mappe per l'uguaglianza. Se il tuo codice di produzione restituisce effettivamente un Map<K, V> potresti voler aggiungere un assegno per le chiavi assertThat(results).containsOnlyKeys((K[]) expectations.keySet().toArray());