2010-04-05 3 views
12

Je dois envoyer une valeur spécifique à partir d'un objet maquette en fonction d'une valeur de clé spécifique.En utilisant Mockito, comment puis-je faire correspondre la paire clé-valeur d'une carte?

De la classe concrète:

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

De le cas de test:

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

Comment puis-je me moque de cette méthode appel pour cette paire de valeurs clé?

Répondre

4

Cela ne fonctionne-t-il pas?

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

Le paramètre Map doit se comporter de la même manière que d'autres paramètres.

+0

Il manque une patte de fermeture. – stefanglase

+0

IOurXMLDocument représente notre couche de service que je ne veux pas appeler pour mes tests unitaires. Pour une situation, nous l'appelons deux fois avec deux valeurs de carte différentes. Au lieu de cela, je veux inspecter la valeur et retourner un résultat fixe. Ainsi, lorsque le code de l'application est le suivant: map.put ("xpath", "PRODUCTNAME"); quand (mock.search (map)) then thenReturn ("Candybar"); – Sean

+0

Ne devrait pas être mock.search (eq (map)) afin de vérifier l'égalité réelle de la carte? – fikovnik

2

On dirait que ce dont vous avez besoin est 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; 
     } 
    } 
}); 

Mais ce qui semble être une meilleure idée est de ne pas utiliser Map primitive en tant que paramètre à votre méthode de recherche - vous pouvez transformer probablement cette carte dans un POJO avec price et productName attributs. Juste une idée :)

15

J'ai trouvé cela en essayant de résoudre un problème similaire en créant un moignon Mockito avec un paramètre Map. Je ne voulais pas écrire un matcher personnalisé pour la carte en question, puis j'ai trouvé une solution plus élégante: utiliser les matchers supplémentaires hamcrest-library avec argThat de Mockito:

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

Si vous devez vérifier contre plusieurs entrées puis vous pouvez utiliser d'autres goodies hamcrest:

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

Cela commence à être longue avec des cartes non triviales, donc je fini par extraire les méthodes pour recueillir les matchers d'entrée et les coincés dans nos 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()]); 
} 

Je me reste:

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

Il y a une certaine laideur associée aux médicaments génériques et je suis supprimer un avertissement, mais au moins il est sec et nichés dans le TestUtil. Une dernière note, faites attention à embedded hamcrest issues in JUnit 4.10. Avec Maven, je recommande d'abord d'importer hamcrest-library puis JUnit 4.11 (maintenant 4.12) et d'exclure hamcrest-core de JUnit pour faire bonne mesure:

<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 septembre 2017 - Par certains des commentaires, je mis à jour ma réponse pour montrer ma dépendance Mockito, mes importations l'util de test, et un JUnit qui est en cours d'exécution vert d'aujourd'hui:

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

C'est une solution pratique. Curieusement cependant, j'obtenais un "type d'argument Wrong, m'attendant à ce que Map ait trouvé Map "que java ne semblait pas résoudre avec un certain nombre d'aiguillages et d'import. J'ai dû changer la méthode de ma classe moqueuse pour accepter une «carte»? extends Chaîne,? extends String> 'pour le faire fonctionner. Des pensées? J'ai les mêmes versions de dépendance pour junit et hamcrest que vous; mon Mockito est v. 1.9.5. –

+0

@PatrickM avez-vous réussi à résoudre le problème des génériques de manière plus propre? De ce qui précède, j'ai exactement le même problème, mais je suis incapable de changer les types génériques de collection ... –

+1

@AndrewEells, non, je n'ai jamais trouvé une solution. Je suis passé de Hamcrest et j'ai commencé à utiliser AssertJ, qui semble avoir besoin de beaucoup moins de coercition pour ses affirmations. –

6

Si vous voulez juste « match » contre une carte particulière, vous pouvez utiliser certaines des réponses ci-dessus, ou une coutume « matcher » qui s'étend Carte, ou un ArgumentCaptor, comme ceci:

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. 

Voir aussi d'autres réponses ici: Verify object attribute value with mockito

+1

c'est exactement ce que je cherchais. Merci. – bitoiu

Questions connexes