2010-04-05 11 views
12

Devo inviare un valore specifico da un oggetto fittizio basato su un valore chiave specifico.Utilizzo di Mockito, come si combacia con la coppia chiave-valore di una mappa?

Dalla classe concreta:

map.put("xpath", "PRICE"); 
search(map); 

Dal banco di prova:

IOurXMLDocument mock = mock(IOurXMLDocument.class); 
when(mock.search(.....need help here).thenReturn("$100.00"); 

Come faccio beffe di questa chiamata al metodo per questa coppia chiave-valore?

risposta

4

Non funziona?

Map<String, String> map = new HashMap<String, String>(); 
map.put("xpath", "PRICE"); 
when(mock.search(map)).thenReturn("$100.00"); 

Il parametro Map dovrebbe comportarsi allo stesso modo di altri parametri.

+0

Ci manca una parentesi di chiusura. – stefanglase

+0

IOurXMLDocument rappresenta il nostro livello di servizio che non desidero chiamare per i miei test di unità. Per 1 situazione, la chiamiamo due volte con 2 valori di mappa diversi. Invece voglio controllare il valore e restituire un risultato fisso. Quindi, quando il codice dell'applicazione procede come segue: map.put ("xpath", "PRODUCTNAME"); quando (mock.search (mappa)). ThenReturn ("Candybar"); – Sean

+0

Non dovrebbe essere mock.search (eq (mappa)) in modo da controllare l'effettiva uguaglianza della mappa? – fikovnik

2

Sembra come quello che vi serve è un Answer:

IOurXMLDocument doc = mock(IOurXMLDocument.class); 
when(doc.search(Matchers.<Map<String,String>>any())).thenAnswer(new Answer<String>() { 
    @Override 
    public String answer(InvocationOnMock invocation) throws Throwable { 
     Map<String, String> map = (Map<String, String>) invocation.getArguments()[0]; 
     String value = map.get("xpath"); 
     if ("PRICE".equals(value)) { 
      return "$100.00"; 
     } else if ("PRODUCTNAME".equals(value)) { 
      return "Candybar"; 
     } else { 
      return null; 
     } 
    } 
}); 

Ma quello che sembra una migliore idea è di non utilizzare primitiva Map come parametro per il metodo di ricerca - probabilmente si potrebbe trasformare questa mappa in un POJO con price e productName attributi. Solo un'idea :)

15

Ho trovato questo cercando di risolvere un problema simile creando uno stub Mockito con un parametro Map. Non volevo scrivere un matcher personalizzato per la mappa in questione e poi ho trovato una soluzione più elegante: utilizzare i matchers supplementari in hamcrest-library con argThat di Mockito:

when(mock.search(argThat(hasEntry("xpath", "PRICE"))).thenReturn("$100.00"); 

Se avete bisogno di controllare contro più voci poi è possibile utilizzare altre chicche hamcrest:

when(mock.search(argThat(allOf(hasEntry("xpath", "PRICE"), hasEntry("otherKey", "otherValue")))).thenReturn("$100.00"); 

Questo inizia ad arrivare a lungo con le mappe non banali, così ho finito per l'estrazione di metodi per raccogliere i matchers entrata e li bloccati nelle nostre TestUtils:

import static org.hamcrest.Matchers.allOf; 
import static org.hamcrest.Matchers.anyOf; 
import static org.hamcrest.Matchers.hasEntry; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Map; 

import org.hamcrest.Matcher; 
--------------------------------- 
public static <K, V> Matcher<Map<K, V>> matchesEntriesIn(Map<K, V> map) { 
    return allOf(buildMatcherArray(map)); 
} 

public static <K, V> Matcher<Map<K, V>> matchesAnyEntryIn(Map<K, V> map) { 
    return anyOf(buildMatcherArray(map)); 
} 

@SuppressWarnings("unchecked") 
private static <K, V> Matcher<Map<? extends K, ? extends V>>[] buildMatcherArray(Map<K, V> map) { 
    List<Matcher<Map<? extends K, ? extends V>>> entries = new ArrayList<Matcher<Map<? extends K, ? extends V>>>(); 
    for (K key : map.keySet()) { 
     entries.add(hasEntry(key, map.get(key))); 
    } 
    return entries.toArray(new Matcher[entries.size()]); 
} 

Quindi mi rimane:

when(mock.search(argThat(matchesEntriesIn(map))).thenReturn("$100.00"); 
when(mock.search(argThat(matchesAnyEntryIn(map))).thenReturn("$100.00"); 

C'è qualche bruttura associato ai farmaci generici e sto sopprimendo un avvertimento, ma almeno è asciutto e nascosto nella TestUtil.

Un'ultima nota, attenzione allo embedded hamcrest issues in JUnit 4.10. Con Maven, consiglio di importare prima la libreria hamcrest e poi la JUnit 4.11 (ora 4.12) ed escludono hamcrest-core da JUnit per buona misura:

<dependency> 
    <groupId>org.hamcrest</groupId> 
    <artifactId>hamcrest-library</artifactId> 
    <version>1.3</version> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>junit</groupId> 
    <artifactId>junit</artifactId> 
    <version>4.12</version> 
    <scope>test</scope> 
    <exclusions> 
     <exclusion> 
      <groupId>org.hamcrest</groupId> 
      <artifactId>hamcrest-core</artifactId> 
     </exclusion> 
    </exclusions> 
</dependency> 
<dependency> 
    <groupId>org.mockito</groupId> 
    <artifactId>mockito-all</artifactId> 
    <version>1.9.5</version> 
    <scope>test</scope> 
</dependency> 

Edit: 1 SETTEMBRE 2017 - Per alcuni dei commenti, ho aggiornato la mia risposta per mostrare la mia dipendenza Mockito, le mie importazioni in l'util di prova, e un JUnit che esegue verde a partire da oggi:

import static blah.tool.testutil.TestUtil.matchesAnyEntryIn; 
import static blah.tool.testutil.TestUtil.matchesEntriesIn; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.is; 
import static org.mockito.Matchers.argThat; 
import static org.mockito.Mockito.mock; 
import static org.mockito.Mockito.when; 

import java.util.HashMap; 
import java.util.Map; 

import org.junit.Test; 

public class TestUtilTest { 

    @Test 
    public void test() { 
     Map<Integer, String> expected = new HashMap<Integer, String>(); 
     expected.put(1, "One"); 
     expected.put(3, "Three"); 

     Map<Integer, String> actual = new HashMap<Integer, String>(); 
     actual.put(1, "One"); 
     actual.put(2, "Two"); 

     assertThat(actual, matchesAnyEntryIn(expected)); 

     expected.remove(3); 
     expected.put(2, "Two"); 
     assertThat(actual, matchesEntriesIn(expected)); 
    } 

    @Test 
    public void mockitoTest() { 
     SystemUnderTest sut = mock(SystemUnderTest.class); 
     Map<Integer, String> expected = new HashMap<Integer, String>(); 
     expected.put(1, "One"); 
     expected.put(3, "Three"); 

     Map<Integer, String> actual = new HashMap<Integer, String>(); 
     actual.put(1, "One"); 

     when(sut.search(argThat(matchesAnyEntryIn(expected)))).thenReturn("Response"); 
     assertThat(sut.search(actual), is("Response")); 
    } 

    protected class SystemUnderTest { 
     // We don't really care what this does 
     public String search(Map<Integer, String> map) { 
      if (map == null) return null; 
      return map.get(0); 
     } 
    } 
} 
+2

Questa è una soluzione a portata di mano. Curiosamente, stavo ottenendo un "Tipo di argomento errato, aspettando Map trovato Map "che java non sembra risolvere con una certa quantità di pungolo e importazione di tweaking. Ho dovuto cambiare il metodo della mia classe derisa per accettare una 'Mappa 'per farlo funzionare. qualche idea? Ho le stesse versioni di dipendenza per junit e hamcrest come te; il mio Mockito è v. 1.9.5. –

+0

@PatrickM sei riuscito a risolvere il problema dei generici in modo più pulito? Da quanto sopra ho esattamente lo stesso problema, ma non riesco a cambiare la collezione di tipi generici ... –

+1

@AndrewEells, no, non ho mai trovato una soluzione. Sono passato da Hamcrest e ho iniziato ad usare AssertJ, che sembra aver bisogno di molto meno tipo coercizione per le sue asserzioni. –

6

Se si vuole solo "match" contro una particolare mappa, è possibile utilizzare per alcune delle risposte di cui sopra, o un "matcher" personalizzato che estende Map, o un ArgumentCaptor, come questo:

ArgumentCaptor<Map> argumentsCaptured = ArgumentCaptor.forClass(Map.class); 
verify(mock, times(1)).method((Map<String, String>) argumentsCaptured.capture()); 
assert argumentsCaptured.getValue().containsKey("keyname"); 
// .getValue() will be the Map it called it with. 

Vedi anche più risposte qui: Verify object attribute value with mockito

+1

questo è esattamente quello che stavo cercando. Grazie. – bitoiu

Problemi correlati