java - Wie kann ich eine Methode ein Argument zurückgeben, das an sie übergeben wurde?


Betrachten Sie eine Methodensignatur wie:

public String myFunction(String abc);

Kann Mockito helfen, dieselbe Zeichenkette zurückzugeben, die die Methode erhalten hat?




Answers


Sie können eine Antwort in Mockito erstellen. Nehmen wir an, wir haben eine Schnittstelle namens Application mit einer Methode myFunction.

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

Hier ist die Testmethode mit einer Mockito-Antwort:

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

Seit Mockito 1.9.5 und Java 8 gibt es einen noch einfacheren Weg mit Lambda-Funktionen:

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




Wenn Sie Mockito 1.9.5 oder höher haben, gibt es eine neue statische Methode, die das Answer für Sie machen kann. Sie müssen etwas schreiben wie

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

oder alternativ

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

Beachten Sie, dass die Methode returnsFirstArg() in der Klasse AdditionalAnswers statisch ist, was für Mockito 1.9.5 neu ist. Sie benötigen also den richtigen statischen Import.




Mit Java 8 ist es möglich, auch bei älteren Versionen von Mockito eine einzeilige Antwort zu erstellen:

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

Natürlich ist dies nicht so nützlich wie die Verwendung von AdditionalAnswers , die von David Wallace vorgeschlagen wurden, aber es könnte nützlich sein, wenn Sie das Argument "on the fly" transformieren möchten.




Ich hatte ein sehr ähnliches Problem. Das Ziel war, einen Service zu verspotten, der Objekte beibehält und sie mit ihrem Namen zurückgeben kann. Der Dienst sieht so aus:

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

Der Service-Mock verwendet eine Karte zum Speichern der Rauminstanzen.

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

Wir können jetzt unsere Tests an diesem Mock ausführen. Beispielsweise:

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



Mit Java 8 kann Steves Antwort

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: Noch kürzer:

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



Ich benutze etwas Ähnliches (im Grunde ist es der gleiche Ansatz). Manchmal ist es sinnvoll, dass ein Mock-Objekt für bestimmte Eingaben eine vordefinierte Ausgabe zurückgibt. Das geht so:

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



Sie können verify () in Kombination mit dem ArgumentCaptor verwenden, um die Ausführung im Test zu gewährleisten, und ArgumentCaptor, um die Argumente auszuwerten:

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

Der Wert des Arguments ist offensichtlich über die Argument.getValue () für weitere Manipulation / Überprüfung / was auch immer zugänglich.