2010-04-05 5 views
12

Ich muss einen bestimmten Wert von einem Mock-Objekt basierend auf einem bestimmten Schlüsselwert senden.Wie kann ich mit Mockito eine Übereinstimmung mit dem Schlüssel/Wert-Paar einer Karte erzielen?

Von der konkreten Klasse:

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

aus dem Testfall:

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

Wie verspotten ich diesen Methodenaufruf für diesen Schlüssel Wertpaar?

Antwort

4

Funktioniert das nicht?

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

Die Map Parameter genauso wie andere Parameter verhalten sollen.

+0

Es ist ein Schließbügel fehlt. – stefanglase

+0

IOurXMLDocument steht für unsere Serviceebene, die ich für meine Komponententests nicht aufrufen möchte. Für 1 Situation rufen wir sie zweimal mit 2 verschiedenen Kartenwerten an. Stattdessen möchte ich den Wert überprüfen und ein festes Ergebnis zurückgeben. Also, wenn der Anwendungscode folgendes tut: map.put ("xpath", "PRODUCTNAME"); when (mock.search (map)). ThenReturn ("Candybar"); – Sean

+0

Sollte nicht mock.search (eq (map)) sein, damit es die tatsächliche Kartengleichheit überprüft? – fikovnik

2

Scheint wie das, was Sie brauchen, ist ein 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; 
     } 
    } 
}); 

Aber was scheint wie eine bessere Idee ist nicht primitiv Map als Parameter zu Ihrer Suchmethode zu verwenden - Sie wahrscheinlich diese Karte in eine pojo verwandeln konnten mit price und productName Attribute. Nur eine Idee :)

15

Ich habe versucht, ein ähnliches Problem zu lösen, indem ich einen Mockito-Stub mit einem Map-Parameter erstelle. Ich wollte nicht einen benutzerdefinierten Matcher für die Karte in Frage schreiben und dann fand ich eine elegantere Lösung: Verwenden Sie die zusätzlichen Matcher in hamcrest-library mit Mockito der argThat:

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

Wenn Sie gegen mehrere Einträge überprüfen, müssen dann Sie können andere hamcrest Leckereien verwenden:

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

Diese lange mit nicht-trivialen Karten zu bekommen beginnt, so dass ich am Ende Methoden Extrahieren der Eintrag Matcher zu sammeln und steckte sie in unserem 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()]); 
} 
einige Hässlichkeit in der TestUtil mit den Generika assoziiert Es gibt und ich unterdrückt eine Warnung, aber zumindest ist es trocken und versteckt

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

:

Also ich bin mit links.

Eine letzte Anmerkung, hüten Sie sich die embedded hamcrest issues in JUnit 4.10. Mit Maven empfehle ich zuerst hamcrest-library und dann JUnit 4.11 (jetzt 4.12) und verstehen sich inklusive hamcrest-Kern von JUnit nur für eine gute Maßnahme:

<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. September 2017 - Per einige der Kommentare, aktualisiert ich meine Antwort meine Mockito Abhängigkeit zu zeigen, meine Importe der Test util und eine junit, die grün wie heute läuft:

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

Das ist eine praktische Lösung. Seltsamerweise erhielt ich jedoch einen "Falschen Argumenttyp, erwartete Map Map gefunden? Extend String,? Extends String>", dass Java nicht mit irgendeiner Menge an Andeutungen aufzulösen schien und Tweaking importierte. Ich musste die Methode meiner verspotteten Klasse ändern, um eine 'Map ', um es zum Laufen zu bringen. Irgendwelche Gedanken? Ich habe die gleichen Abhängigkeitsversionen für junit und hamcrest wie du; mein Mockito ist V. 1.9.5. –

+0

@PatrickM haben Sie es geschafft, das Generika-Problem sauberer zu lösen? Von dem oben genannten habe ich genau das gleiche Problem, aber ich bin nicht in der Lage, die Sammlung generische Typen zu ändern ... –

+1

@AndrewEells, nein, ich habe nie eine Lösung gefunden. Ich wechselte von Hamcrest und begann AssertJ zu verwenden, was anscheinend viel weniger Typenzwang für seine Behauptungen benötigt. –

6

Wenn Sie nur gegen eine bestimmte Karte zu „match“ möchten, können Sie über einige der Antworten verwenden oder ein benutzerdefinierter „Matcher“ Das erweitert Map oder einen ArgumentCaptor wie folgt:

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. 

Weitere Antworten auch hier: Verify object attribute value with mockito

+1

genau das habe ich gesucht. Vielen Dank. – bitoiu