"Как сделать так, чтобы имитируемый метод возвращал аргумент, который был ему передан?"
Проблема:
У меня есть метод с таким сигнатурой:
public String myFunction(String abc);
Мне нужно, чтобы Mockito возвращал ту же строку, которую метод получает в качестве аргумента. Может ли Mockito помочь в этом? Если да, то как правильно настроить моки для достижения этого результата?
5 ответ(ов)
Начиная с Mockito 1.9.5+ и Java 8+
Вы можете использовать лямбда-выражение следующим образом:
when(myMock.myFunction(anyString())).thenAnswer(i -> i.getArguments()[0]);
Где
i
является экземпляромInvocationOnMock
.
Для более старых версий
Вы можете создать ответ (Answer) в Mockito. Допустим, у нас есть интерфейс с названием MyInterface, содержащий метод myFunction.
public interface MyInterface {
public String myFunction(String abc);
}
Вот пример тестового метода с использованием ответа Mockito:
public void testMyFunction() throws Exception {
MyInterface mock = mock(MyInterface.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"));
}
Таким образом, вы можете адаптировать своё использование Mockito в зависимости от версии библиотеки и языка.
Если у вас есть Mockito версии 1.9.5 или выше, то появился новый статический метод, который может автоматически создавать объект Answer
для вас. Вам нужно написать что-то вроде:
import static org.mockito.Mockito.when;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
when(myMock.myFunction(anyString())).then(returnsFirstArg());
или альтернативно:
doAnswer(returnsFirstArg()).when(myMock).myFunction(anyString());
Обратите внимание, что метод returnsFirstArg()
является статическим в классе AdditionalAnswers
, который появился в Mockito начиная с версии 1.9.5. Поэтому убедитесь, что вы добавили правильный статический импорт.
С Java 8 это возможно сделать в одну строку, даже с более старыми версиями Mockito:
when(myMock.myFunction(anyString())).then(i -> i.getArgumentAt(0, String.class));
Конечно, это не так полезно, как использование AdditionalAnswers
, предложенное Дэвидом Уоллесом, но может оказаться полезным, если вам нужно преобразовать аргумент "на лету".
У меня была очень похожая проблема. Цель заключалась в том, чтобы создать мок-сервис, который сохраняет объекты и может возвращать их по имени. Сервис выглядит следующим образом:
public class RoomService {
public Room findByName(String roomName) {...}
public void persist(Room room) {...}
}
Мок-сервис использует карту для хранения экземпляров Room.
RoomService roomService = mock(RoomService.class);
final Map<String, Room> roomMap = new HashMap<String, Room>();
// мок для метода 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));
// мок для метода 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;
}
});
Теперь мы можем запускать наши тесты на этом моке. Например:
String name = "room";
Room room = new Room(name);
roomService.persist(room);
assertThat(roomService.findByName(name), equalTo(room));
assertNull(roomService.findByName("none"));
Таким образом, мы создали простую и эффективную имитацию сервиса, которая позволяет нам проверять функциональность сохранения и поиска объектов Room.
Эта вопрос уже довольно старый, но, думаю, все еще актуален. При этом, принятый ответ работает только со строками. Учитывая, что есть Mockito 2.1 и некоторые импорты изменились, хотел бы поделиться своим текущим решением:
import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@Mock
private MyClass myClass;
// это будет возвращать всё, что вы передаете, но это довольно нереалистично
when(myClass.myFunction(any())).then(returnsFirstArg());
// более "жизненно" будет принимать только нужный тип
when(myClass.myFunction(any(ClassOfArgument.class))).then(returnsFirstArg());
Метод myClass.myFunction
будет выглядеть так:
public class MyClass {
public ClassOfArgument myFunction(ClassOfArgument argument){
return argument;
}
}
Надеюсь, это поможет!
Как замокировать методы с возвращаемым типом void с помощью Mockito
Как проверить, что конкретный метод не был вызван, используя Mockito?
Как работают сервлеты? Инстанцирование, сессии, общие переменные и многопоточность
Возможные значения конфигурации hbm2ddl.auto в Hibernate и их назначение
Что такое PECS (Producer Extends Consumer Super)?