2010-04-21 10 views
466

Tenir compte d'une signature de la méthode comme:Comment puis-je rendre une méthode retourne un argument qui lui a été passé?

public String myFunction(String abc); 

Mockito peut aider à retourner la même chaîne que la méthode reçue?

+0

Ok, que diriez-vous de n'importe quel cadre de jocking de java en général ... Est-ce possible avec n'importe quel autre cadre, ou devrais-je juste créer un bout stupide pour imiter le comportement que je veux? –

Répondre

697

Vous pouvez créer une réponse dans Mockito. Supposons que nous ayons une interface nommée Application avec une méthode myFunction.

public interface Application { 
    public String myFunction(String abc); 
} 

Voici la méthode d'essai avec une réponse Mockito:

public void testMyFunction() throws Exception { 
    Application mock = mock(Application.class); 
    when(mock.myFunction(anyString())).thenAnswer(new Answer<String>() { 
    @Override 
    public String answer(InvocationOnMock invocation) throws Throwable { 
     Object[] args = invocation.getArguments(); 
     return (String) args[0]; 
    } 
    }); 

    assertEquals("someString",mock.myFunction("someString")); 
    assertEquals("anotherString",mock.myFunction("anotherString")); 
} 

Depuis Mockito 1.9.5 et Java 8 il existe un moyen encore plus facile en utilisant des fonctions lambda:

when(myMock.myFunction(anyString())).thenAnswer(i -> i.getArguments()[0]);

+0

C'est aussi ce que je cherchais. Je vous remercie! Mon problème était différent, cependant. Je veux me moquer d'un service de persistance (EJB) qui stocke des objets et les renvoie par leur nom. – migu

+1

Super, m'a sauvé beaucoup de temps aussi – Mat

+0

Je pense qu'il devrait y avoir une façon plus simple d'utiliser la capture d'argument ou quelque chose ... – iwein

5

J'utilise quelque chose de similaire (fondamentalement c'est la même approche). Parfois, il est utile d'avoir un objet simulé qui retourne une sortie prédéfinie pour certaines entrées. Cela se passe comme ceci:

private Hashtable<InputObject, OutputObject> table = new Hashtable<InputObject, OutputObject>(); 
table.put(input1, ouput1); 
table.put(input2, ouput2); 

... 

when(mockObject.method(any(InputObject.class))).thenAnswer(
     new Answer<OutputObject>() 
     { 
      @Override 
      public OutputObject answer(final InvocationOnMock invocation) throws Throwable 
      { 
       InputObject input = (InputObject) invocation.getArguments()[0]; 
       if (table.containsKey(input)) 
       { 
        return table.get(input); 
       } 
       else 
       { 
        return null; // alternatively, you could throw an exception 
       } 
      } 
     } 
     ); 
33

J'ai eu un problème très similaire. L'objectif était de se moquer d'un service qui persiste sur les Objets et de pouvoir les renvoyer par leur nom. Le service ressemble à ceci:

public class RoomService { 
    public Room findByName(String roomName) {...} 
    public void persist(Room room) {...} 
} 

Le service de simulation utilise une carte pour stocker les instances de pièce.

RoomService roomService = mock(RoomService.class); 
final Map<String, Room> roomMap = new HashMap<String, Room>(); 

// mock for method persist 
doAnswer(new Answer<Void>() { 
    @Override 
    public Void answer(InvocationOnMock invocation) throws Throwable { 
     Object[] arguments = invocation.getArguments(); 
     if (arguments != null && arguments.length > 0 && arguments[0] != null) { 
      Room room = (Room) arguments[0]; 
      roomMap.put(room.getName(), room); 
     } 
     return null; 
    } 
}).when(roomService).persist(any(Room.class)); 

// mock for method findByName 
when(roomService.findByName(anyString())).thenAnswer(new Answer<Room>() { 
    @Override 
    public Room answer(InvocationOnMock invocation) throws Throwable { 
     Object[] arguments = invocation.getArguments(); 
     if (arguments != null && arguments.length > 0 && arguments[0] != null) { 
      String key = (String) arguments[0]; 
      if (roomMap.containsKey(key)) { 
       return roomMap.get(key); 
      } 
     } 
     return null; 
    } 
}); 

Nous pouvons maintenant exécuter nos tests sur ce modèle. Par exemple:

String name = "room"; 
Room room = new Room(name); 
roomService.persist(room); 
assertThat(roomService.findByName(name), equalTo(room)); 
assertNull(roomService.findByName("none")); 
391

Si vous avez Mockito 1.9.5 ou plus, il y a une nouvelle méthode statique qui peut faire l'objet Answer pour vous. Vous devez écrire quelque chose comme

when(myMock.myFunction(anyString())).then(returnsFirstArg()); 

ou bien

doAnswer(returnsFirstArg()).when(myMock).myFunction(anyString()); 

Notez que la méthode returnsFirstArg() est statique dans la classe AdditionalAnswers, ce qui est nouveau pour Mockito 1.9.5; Vous aurez donc besoin de la bonne importation statique.

+2

en passant toutes les réponses ce 1 est le meilleur ... ne sait pas pourquoi il n'est pas accepté 1 – Nimrod007

+13

Peut-être parce que la réponse acceptée est antérieure à Mockito 1.9.5 – Jay

+7

Note: c'est 'quand (...). then (returnsFirstArg())', j'avais par erreur ' when (...). thenReturn (returnsFirstArg()) 'qui a donné' java.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt ne peut pas être converti en ' –

36

Avec Java 8, il est possible de créer une réponse d'une ligne, même avec la version plus ancienne de Mockito:

when(myMock.myFunction(anyString()).then(i -> i.getArgumentAt(0, String.class)); 

Bien sûr, ce n'est pas aussi utile que l'utilisation AdditionalAnswers suggéré par David Wallace, mais pourrait être utile si vous voulez transformer l'argument "à la volée".

+1

Brillant. Je vous remercie. Si l'argument est 'long', cela peut-il fonctionner avec boxing et' Long.class'? – vikingsteve

24

Avec Java 8, Steve's answer peut devenir

public void testMyFunction() throws Exception { 
    Application mock = mock(Application.class); 
    when(mock.myFunction(anyString())).thenAnswer(
    invocation -> { 
     Object[] args = invocation.getArguments(); 
     return args[0]; 
    }); 

    assertEquals("someString", mock.myFunction("someString")); 
    assertEquals("anotherString", mock.myFunction("anotherString")); 
} 

EDIT: encore plus court:

public void testMyFunction() throws Exception { 
    Application mock = mock(Application.class); 
    when(mock.myFunction(anyString())).thenAnswer(
     invocation -> invocation.getArgument(0)); 

    assertEquals("someString", mock.myFunction("someString")); 
    assertEquals("anotherString", mock.myFunction("anotherString")); 
} 
+1

Encore plus court^_^ '' when (mock.myFunction (anyString())). ThenAnswer (invocation -> invocation.getArgument (0)); '' –

+0

Ha fantastique. Edité pour refléter cela ^ – yiwei

1

Vous pouvez utiliser verify() en combinaison avec le ArgumentCaptor pour assurer l'exécution dans le test et ArgumentCaptor pour évaluer les arguments:

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class); 
verify(mock).myFunction(argument.capture()); 
assertEquals("the expected value here", argument.getValue()); 

L'argument ' La valeur de s est évidemment accessible via le paramètre argument.getValue() pour d'autres manipulations/vérifications/quoi que ce soit.