web-dev-qa-db-fra.com

Faire en sorte qu'une méthode simulée retourne un argument qui lui a été transmis

Considérons une signature de méthode comme:

public String myFunction(String abc);

Mockito peut-il aider à renvoyer la même chaîne que celle reçue par la méthode?

597
Abhijeet Kashnia

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 de test 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, utiliser les fonctions lambda est encore plus simple:

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

902
Steve

Si vous utilisez Mockito 1.9.5 ou une version ultérieure, il existe une nouvelle méthode statique permettant de créer l’objet Answer. Vous devez écrire quelque chose comme

import static org.mockito.Mockito.when;
import static org.mockito.AdditionalAnswers.returnsFirstArg;

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, qui est nouvelle pour Mockito 1.9.5; vous aurez donc besoin de la bonne importation statique.

508
Dawood ibn Kareem

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

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

Bien sûr, cela n’est pas aussi utile que d’utiliser AdditionalAnswers suggéré par David Wallace, mais cela pourrait être utile si vous voulez transformer un argument "à la volée".

59
Paweł Dyda

J'ai eu un problème très similaire. Le but était de simuler un service qui persiste des objets et peut les renvoyer par leur nom. Le service ressemble à ceci:

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

La maquette de service utilise une carte pour stocker les instances de salle.

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 cette maquette. Par exemple:

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

Avec Java 8, réponse de Steve 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"));
}
29
yiwei

J'utilise quelque chose de similaire (fondamentalement, c'est la même approche). Parfois, il est utile qu'un objet factice renvoie des résultats prédéfinis pour certaines entrées. Cela va 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
               }
           }
       }
       );
5
martin

C'est une question assez ancienne mais je pense toujours pertinente. De plus, la réponse acceptée ne fonctionne que pour String. En attendant, il y a Mockito 2.1 et certaines importations ont changé. J'aimerais donc partager ma réponse actuelle:

import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@Mock
private MyClass myClass;

// this will return anything you pass, but it's pretty unrealistic
when(myClass.myFunction(any())).then(returnsFirstArg());
// it is more "life-like" to accept only the right type
when(myClass.myFunction(any(ClassOfArgument.class))).then(returnsFirstArg());

La myClass.myFunction ressemblerait à ceci:

public class MyClass {
    public ClassOfArgument myFunction(ClassOfArgument argument){
        return argument;
    }  
}
2
LazR

Vous voudrez peut-être utiliser verify () en combinaison avec 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());

La valeur de l'argument est évidemment accessible via argument.getValue () pour des manipulations/vérifications/autres quelconques.

1
fl0w