java - Come posso rendere un metodo restituire un argomento che è stato passato ad esso?


Prendi in considerazione una firma del metodo come:

public String myFunction(String abc);

Mockito può restituire la stessa stringa ricevuta dal metodo?




Answers


Puoi creare una risposta in Mockito. Supponiamo di avere un'interfaccia denominata Applicazione con un metodo myFunction.

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

Ecco il metodo di prova con una risposta 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"));
}

Da Mockito 1.9.5 e Java 8 c'è un modo ancora più semplice usando le funzioni lambda:

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




Se hai Mockito 1.9.5 o versioni successive, c'è un nuovo metodo statico che può rendere l'oggetto Answer per te. Devi scrivere qualcosa del genere

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

o in alternativa

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

Si noti che il metodo returnsFirstArg() è statico nella classe AdditionalAnswers , che è nuova a Mockito 1.9.5; quindi avrai bisogno della giusta importazione statica.




Con Java 8 è possibile creare una risposta a una riga anche con la versione precedente di Mockito:

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

Ovviamente questo non è tanto utile quanto usare AdditionalAnswers suggerito da David Wallace, ma potrebbe essere utile se si desidera trasformare l'argomento "al volo".




Ho avuto un problema molto simile. L'obiettivo era quello di prendere in giro un servizio che persiste Oggetti e può restituirli con il loro nome. Il servizio si presenta così:

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

Il servizio fittizio utilizza una mappa per memorizzare le istanze 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;
    }
});

Ora possiamo eseguire i nostri test su questa simulazione. Per esempio:

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



Con Java 8, la risposta di Steve può diventare

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: ancora più breve:

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



Io uso qualcosa di simile (fondamentalmente è lo stesso approccio). A volte è utile avere un output predefinito di un oggetto mock per determinati input. Questo va così:

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



Potresti voler usare verify () in combinazione con ArgumentCaptor per assicurare l'esecuzione nel test e ArgumentCaptor per valutare gli argomenti:

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

Il valore dell'argomento è ovviamente accessibile tramite argument.getValue () per ulteriori manipolazioni / verifiche / quant'altro.