使模拟方法返回传递给它的参数


673

考虑如下方法签名:

public String myFunction(String abc);

Mockito可以帮助返回该方法收到的相同字符串吗?


好的,一般来说,任何Java模拟框架都可以...其他任何框架都可以做到吗?还是我应该创建一个哑巴桩来模仿我想要的行为?
Abhijeet Kashnia'4

Answers:


1001

您可以在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开始,您还可以使用lambda表达式:

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

1
这也是我一直在寻找的东西。谢谢!我的问题却不同。我想模拟存储对象并按名称返回它们的持久性服务(EJB)。
migu 2011年

7
我创建了一个额外的类,用于包装答案的创建。因此代码看起来像是when(...).then(Return.firstParameter())
SpaceTrucker 2012年

69
使用Java 8 lambda,即使对于特定的类(即),也很容易返回第一个参数when(foo(any()).then(i -> i.getArgumentAt(0, Bar.class))。您也可以使用方法引用并调用真实方法。
帕维尔Dyda

这用一种返回语句的方法解决了我的问题,该方法Iterator<? extends ClassName>导致thenReturn()语句中发生各种类型的转换问题。
Michael Shopsin 2015年

16
在Java 8和Mockito <1.9.5的情况下,Paweł的答案变成了when(foo(any()).thenAnswer(i -> i.getArguments()[0])
Graeme Moss

566

如果您具有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的新增功能。因此您需要正确的静态导入。


17
注意:是when(...).then(returnsFirstArg()),我错误地when(...).thenReturn(returnsFirstArg())给了java.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt cannot be cast to
BenediktKöppel2015年

1
注意:returnFirstArg()返回Answer <>而不是参数的值。尝试调用.thenReturn(new Foo(returnsFirstArg()))时,无法将'Foo(java.lang.String)应用于'(org.mockito.stubbing.Answer <java.lang.Object>)'
55

在过去的几年中,我总是需要一次又一次地在Google上搜索此答案,因为我不记得“ AdditionalAnswers”,而且我很少需要它。然后,我想知道由于找不到必要的依赖关系而导致如何建立这种情况。难道不能直接将其直接添加到嘲笑吗?:/
BAERUS

2
史蒂夫的答案更为笼统。这个仅允许您返回原始参数。如果您要处理该参数并返回结果,请遵循史蒂夫的回答规则。我都赞成,因为它们都很有用。
akostadinov

仅供参考,我们必须进口 static org.mockito.AdditionalAnswers.returnsFirstArg。这可以使用returnFirstArg。另外,我可以when(myMock.myFunction(any())).then(returnsFirstArg())在Mockito 2.20中做。*
gtiwari333 '18

77

使用Java 8,即使使用旧版本的Mockito,也可以创建单行答案:

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

当然,这并不像AdditionalAnswersDavid Wallace所建议的那样有用,但是如果您想“即时”转换参数,则可能有用。


1
辉煌。谢谢。如果参数为long,这是否仍适用于拳击和Long.class
vikingsteve'2

1
找不到我的.getArgumentAt(..),但.getArgument(1)起作用了(模拟2.6.2)
Curtis Yallop

41

我有一个非常类似的问题。目的是模拟一个持久化对象并可以按其名称返回的服务。该服务如下所示:

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

34

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

6

这是一个很老的问题,但我认为仍然很重要。同样,可接受的答案仅适用于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;
    }  
}

4

我使用类似的方法(基本上是相同的方法)。有时让模拟对象返回某些输入的预定义输出很有用。就像这样:

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

4

您可能希望将verify()与ArgumentCaptor结合使用以确保在测试中执行,而ArgumentCaptor可以评估参数:

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

很明显,可以通过arguments.getValue()访问该参数的值,以进行进一步的操作/检查/任何操作。


3

这有点旧,但是我来这里是因为我遇到了同样的问题。我正在使用JUnit,但是这次是在带有Mockk的Kotlin应用程序中。我在这里发布了一个示例,以供参考并与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"))
}

2

您可以使用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.
    }
  });

或者,如果您是lambda的粉丝,只需:

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


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

摘要:使用argumentscaptor捕获传递的参数。稍后在回答中返回使用getValue捕获的值。


这行不通(了吗?)。关于文档:必须在验证过程中使用此方法。这意味着你只能捕获使用验证方法时的价值
穆罕默德Misir

1.不确定This doesn´t work (anymore?).我要在我的实例上执行此操作是什么意思。2.对不起,我不清楚您要提出的观点。答案特定于OP的问题。
西里尔·谢里安
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.