java - Comment puis-je rendre une méthode retourne un argument qui lui a été passé?


Considérez une signature de méthode comme:

public String myFunction(String abc);

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




Answers


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, il est encore plus simple d'utiliser les fonctions lambda:

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




Si vous avez Mockito 1.9.5 ou supérieur, il existe une nouvelle méthode statique qui peut rendre 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 , qui est nouvelle dans Mockito 1.9.5; Vous aurez donc besoin de la bonne importation statique.




Avec Java 8, il est possible de créer une réponse d'une ligne même avec une ancienne version 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 de AdditionalAnswers suggéré par David Wallace, mais cela pourrait être utile si vous voulez transformer l'argument "à la volée".




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 simulateur de service utilise une carte pour stocker les instances Room.

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 faire nos tests sur ce simulacre. Par exemple:

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



Avec Java 8, la 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"));
}



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 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
               }
           }
       }
       );



Vous pouvez utiliser verify () en combinaison avec ArgumentCaptor pour assurer l'exécution du 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 d'autres manipulations / vérifications / autres.