Создание фиктивного метода, возвращающего переданный ему аргумент

Рассмотрим сигнатуру метода, например:

public String myFunction(String abc);

Может ли Mockito вернуть ту же строку, что и метод?


person Abhijeet Kashnia    schedule 21.04.2010    source источник
comment
Хорошо, как насчет любого фреймворка mocking java в целом ... Возможно ли это с любым другим фреймворком, или я должен просто создать тупую заглушку, чтобы имитировать поведение, которое я хочу?   -  person Abhijeet Kashnia    schedule 22.04.2010


Ответы (10)


Вы можете создать ответ в Mockito. Предположим, у нас есть интерфейс с именем Application с методом myFunction.

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

Вот метод тестирования с ответом 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"));
}

Начиная с Mockito 1.9.5 и Java 8, вы также можете использовать лямбда-выражение:

when(myMock.myFunction(anyString())).thenAnswer(i -> i.getArguments()[0]);
person Steve    schedule 26.04.2010
comment
Это то, что я тоже искал. Спасибо! Однако моя проблема была в другом. Я хочу имитировать службу постоянства (EJB), которая хранит объекты и возвращает их по имени. - person migu; 19.07.2011
comment
Я создал дополнительный класс, который завершает создание ответа. Таким образом, код выглядит как when(...).then(Return.firstParameter()) - person SpaceTrucker; 26.09.2012
comment
С лямбдами Java 8 ужина легко вернуть первый аргумент, даже для определенного класса, то есть when(foo(any()).then(i -> i.getArgumentAt(0, Bar.class)). Вы также можете использовать ссылку на метод и вызвать реальный метод. - person Paweł Dyda; 29.01.2015
comment
Это решает мою проблему с методом, который возвращает Iterator<? extends ClassName>, который вызывает всевозможные проблемы приведения в операторе thenReturn(). - person Michael Shopsin; 20.03.2015
comment
Как я могу решить эту проблему, если тип возвращаемого значения недействителен public void myFunction(String abc); - person SDS; 11.08.2015
comment
С Java 8 и Mockito ‹1.9.5 тогда ответ Павла становится when(foo(any()).thenAnswer(i -> i.getArguments()[0]) - person Graeme Moss; 31.08.2015
comment
@Steve, я предлагаю обновить ваш ответ, включив в него изменения из Mockito 1.9.5 и / или Java 8. См., Например, мою правку. Я думаю, это может помочь людям легко найти эту информацию. - person borowis; 24.11.2016
comment
TL; DR .thenAnswer { it.arguments[0] } - person Saba; 27.08.2020

Если у вас 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; поэтому вам понадобится правильный статический импорт.

person Dawood ibn Kareem    schedule 07.08.2012
comment
Примечание: это when(...).then(returnsFirstArg()), у меня по ошибке было when(...).thenReturn(returnsFirstArg()), что дало java.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt cannot be cast to - person Benedikt Köppel; 11.03.2015
comment
Примечание: returnsFirstArg () возвращает ответ ‹›, а не значение аргумента. Получил 'Foo (java.lang.String) не может быть применен к' (org.mockito.stubbing.Answer ‹java.lang.Object›) 'при попытке вызвать .thenReturn (new Foo (returnsFirstArg ())) - person Lu55; 18.05.2015
comment
Мне всегда нужно гуглить этот ответ снова и снова в течение последних лет, так как я просто не могу вспомнить AdditionalAnswers и мне он нужен очень редко. Затем мне интересно, как, черт возьми, я могу создать этот сценарий, если я не могу найти необходимые зависимости. Разве это нельзя просто добавить прямо в mockito? : / - person BAERUS; 06.06.2016
comment
Ответ Стива более общий. Это позволяет вам возвращать только необработанный аргумент. Если вы хотите обработать этот аргумент и вернуть результат, тогда правила ответа Стива. Я поддержал оба, поскольку они оба полезны. - person akostadinov; 06.12.2017
comment
К вашему сведению, мы должны импортировать static org.mockito.AdditionalAnswers.returnsFirstArg. это для использования returnFirstArg. Кроме того, я могу делать when(myMock.myFunction(any())).then(returnsFirstArg()) в Mockito 2.20. * - person gtiwari333; 30.07.2018
comment
Я получаю ошибку времени компиляции Несоответствие типа: невозможно преобразовать ответ ‹Object› в коллекцию ‹MyObject›. Может быть, returnFirstArg не работает для дженериков? - person Curtis Yallop; 05.10.2018
comment
@CurtisYallop Вы уверены, что правильно поняли синтаксис? Вы используете doAnswer или then вместо doReturn или thenReturn? Вы правильно поставили круглые скобки? - person Dawood ibn Kareem; 07.10.2018

В Java 8 можно создать однострочный ответ даже в более старой версии Mockito:

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

Конечно, это не так полезно, как использование AdditionalAnswers, предложенное Дэвидом Уоллесом, но может быть полезно, если вы хотите преобразовать аргумент «на лету».

person Paweł Dyda    schedule 04.02.2015
comment
Блестяще. Спасибо. Если аргумент long, может ли это работать с боксом и Long.class? - person vikingsteve; 01.02.2016
comment
.getArgumentAt (..) не был найден для меня, но .getArgument (1) работал (mockito 2.6.2) - person Curtis Yallop; 05.10.2018

У меня была очень похожая проблема. Цель состояла в том, чтобы имитировать службу, которая сохраняет объекты и может возвращать их по их имени. Сервис выглядит так:

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

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

Теперь мы можем запустить наши тесты на этом макете. Например:

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

В Java 8 ответ Стива может стать

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

РЕДАКТИРОВАТЬ: Еще короче:

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"));
}
person yiwei    schedule 24.07.2015
comment
Это круто, но, к сожалению, для thenThrow это не работает (thenThrow не принимает аргумент InvocationOnMock). - person Dmitriy Popov; 22.07.2020

Это довольно старый вопрос, но я думаю, что он все еще актуален. Также принятый ответ работает только для String. Между тем существует 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;

// 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());

Функция myClass.myFunction будет выглядеть так:

public class MyClass {
    public ClassOfArgument myFunction(ClassOfArgument argument){
        return argument;
    }  
}
person LazR    schedule 28.02.2019

Это немного устарело, но я пришел сюда, потому что у меня была такая же проблема. Я использую JUnit, но на этот раз в приложении Kotlin с mockk. Я размещаю здесь образец для справки и сравнения с аналогом на Java:

@Test
fun demo() {
  // mock a sample function
  val aMock: (String) -> (String) = mockk()

  // make it return the same as the argument on every invocation
  every {
    aMock.invoke(any())
  } answers {
    firstArg()
  }

  // test it
  assertEquals("senko", aMock.invoke("senko"))
  assertEquals("senko1", aMock.invoke("senko1"))
  assertNotEquals("not a senko", aMock.invoke("senko"))
}
person Lachezar Balev    schedule 31.10.2019

Этого можно добиться с помощью ArgumentCaptor

Представьте, что у вас есть такая функция bean-компонента.

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

Затем в вашем тестовом классе:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
      return param.getValue();//return the captured value.
    }
  });

ИЛИ, если вам нравится лямбда, просто сделайте:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture()))
    .thenAnswer((invocation) -> param.getValue());

Сводка: используйте аргументcaptor, чтобы зафиксировать переданный параметр. Позже в ответе верните значение, полученное с помощью getValue.

person Cyril Cherian    schedule 01.11.2019
comment
Это не работает (больше?). Что касается документации: этот метод должен использоваться внутри проверки. Это означает, что вы можете получить значение только при использовании метода проверки. - person Muhammed Misir; 10.02.2020
comment
1. Не уверен, что вы имеете в виду под This doesn´t work (anymore?).. У меня это работает на моем экземпляре. 2. Извините, я не совсем понимаю, о чем вы пытаетесь сказать. Ответ относится к вопросу OP. - person Cyril Cherian; 11.02.2020

Возможно, вы захотите использовать verify () в сочетании с ArgumentCaptor, чтобы гарантировать выполнение в тесте, и ArgumentCaptor для оценки аргументов:

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

Очевидно, что значение аргумента доступно через arguments.getValue () для дальнейших манипуляций / проверки / чего угодно.

person fl0w    schedule 28.10.2016

Я использую что-то похожее (в основном тот же подход). Иногда бывает полезно, чтобы фиктивный объект возвращал предопределенный вывод для определенных входов. Это выглядит так:

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
               }
           }
       }
       );
person martin    schedule 29.11.2010