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

考虑一个方法签名,如:

public String myFunction(String abc);

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

490076 次浏览

自Mockito 1.9.5+和Java8+

您可以使用lambda表达式,例如:

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

其中iInvocationOnMock的实例。

对于旧版本

您可以在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>() {@Overridepublic String answer(InvocationOnMock invocation) throws Throwable {Object[] args = invocation.getArguments();return (String) args[0];}});
assertEquals("someString",mock.myFunction("someString"));assertEquals("anotherString",mock.myFunction("anotherString"));}

我使用类似的东西(基本上是相同的方法)。有时让模拟对象为某些输入返回预定义的输出是有用的。如下所示:

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>(){@Overridepublic 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}}});

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

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 persistdoAnswer(new Answer<Void>() {@Overridepublic 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 findByNamewhen(roomService.findByName(anyString())).thenAnswer(new Answer<Room>() {@Overridepublic 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;}});

我们现在可以在此mock上运行测试。例如:

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

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

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

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

当然,这不像David Wallace建议的使用AdditionalAnswers那么有用,但如果您想“动态”转换参数,可能会很有用。

Java8,史蒂夫的回答可以成为

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

您可能希望与ArgumentCaptor结合使用验证()来确保测试中的执行,并使用ArgumentCaptor来评估参数:

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

参数的值显然可以通过argument.getValue()访问,以便进一步操作/检查 /whatever.

这是一个非常古老的问题,但我认为仍然相关。此外,接受的答案仅适用于String。同时还有Mockito 2.1,一些导入已经改变,所以我想分享我目前的答案:

import static org.mockito.AdditionalAnswers.returnsFirstArg;import static org.mockito.ArgumentMatchers.any;import static org.mockito.Mockito.when;
@Mockprivate MyClass myClass;
// this will return anything you pass, but it's pretty unrealisticwhen(myClass.myFunction(any())).then(returnsFirstArg());// it is more "life-like" to accept only the right typewhen(myClass.myFunction(any(ClassOfArgument.class))).then(returnsFirstArg());

myClass.my函数如下所示:

public class MyClass {public ClassOfArgument myFunction(ClassOfArgument argument){return argument;}}

这有点旧,但我来到这里是因为我有同样的问题。我使用JUnit,但这次是在带有mockk的静态编程语言应用程序中。我在这里发布一个示例,以供参考并与Java对应物进行比较:

@Testfun demo() {// mock a sample functionval aMock: (String) -> (String) = mockk()
// make it return the same as the argument on every invocationevery {aMock.invoke(any())} answers {firstArg()}
// test itassertEquals("senko", aMock.invoke("senko"))assertEquals("senko1", aMock.invoke("senko1"))assertNotEquals("not a senko", aMock.invoke("senko"))}

您可以通过使用数据采集来实现这一点

假设你有这样的bean函数。

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

然后在你的测试类中:

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

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

如果你是lambda的粉丝,只需:

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

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

总结:使用artumentcaptor捕获传递的参数。稍后在回答中返回使用getValue捕获的值。