如果我模拟一个方法来返回某个对象的新实例,我该如何捕获返回的实例?
E.g:
when(mock.someMethod(anyString())).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return new Foo(args[0])
}
});
显然,我可以有一个Foo类型的字段,在answer
里面将它设置为新实例,但有更好的方法吗?像ArgumentCaptor?
答案 0 :(得分:19)
我想做类似的事情,但是使用间谍对象而不是模拟。具体来说,给定一个间谍对象,我想捕获返回值。根据{{3}}的回答,这就是我想出的内容。
public class ResultCaptor<T> implements Answer {
private T result = null;
public T getResult() {
return result;
}
@Override
public T answer(InvocationOnMock invocationOnMock) throws Throwable {
result = (T) invocationOnMock.callRealMethod();
return result;
}
}
预期用途:
// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);
// let's capture the return values from spiedDao.find()
final ResultCaptor<QueryResult> resultCaptor = new ResultCaptor<>();
doAnswer(resultCaptor).when(spiedDao).find(any(User.class), any(Query.class));
// execute once
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something */);
/// change conditions ///
// execute again
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something different */);
答案 1 :(得分:9)
看起来您希望观察然后Answer
个实例并在每次调用answer
方法时接收notfications(这会触发创建新的Foo
)。那么为什么不发明一个ObservableAnswer
类:
public abstract class ObservableAnswer implements Answer {
private Listener[] listeners; // to keep it very simple...
public ObservableAnswer(Listener...listeners) {
this.listeners = listeners;
}
@Override
public Object answer(InvocationOnMock invocation) {
Object answer = observedAnswer(invocation);
for (Listener listener:listeners) {
listener.send(answer);
}
return answer;
}
// we'll have to implement this method now
public abstract Object observedAnswer(InvocationOnMock invocation);
}
预期用途:
Listener[] myListenerns = getListeners(); // some magic (as usual)
when(mock.someMethod(anyString())).thenAnswer(new ObservableAnswer(myListeners) {
Object observedAnswer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return new Foo(args[0])
}
});
答案 2 :(得分:0)
作为@JeffFairley答案的替代方法,您可以利用AtomicReference<T>
。它将充当Holder<T>
,但我更喜欢它,而不是真正的持有人,因为它是在Java的基本框架中定义的。
// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);
// let's capture the return values from spiedDao.find()
AtomicReference<QueryResult> reference = new AtomicReference<>();
doAnswer(invocation -> {
QueryResult result = (QueryResult)invocation.callRealMethod();
reference.set(result);
return result;
}).when(spiedDao).find(any(User.class), any(Query.class));
// execute once
service.run();
assertThat(reference.get()).isEqualTo(/* something */);
/// change conditions ///
// execute again
service.run();
assertThat(result.get()).isEqualTo(/* something different */);
我认为:ResultCaptor是很酷的东西,将来可能会集成到Mockito中,可以广泛使用并且语法短。但是,如果您偶尔需要它,那么lambda的几行会更简洁
答案 3 :(得分:0)
调用doAnswer,然后调用真正的方法并将返回值添加到一个列表中,如下:
final var capturedValues = new ArrayList<Integer>();
final var myObjectList = spy(new MyObject());
doAnswer(invocation -> {
final var r = invocation.callRealMethod();
capturedValues.add((Integer) r);
return r;
})
.when(myObjectList)
.mySuperMethod;
一个完整的例子:
@Test
public void test() {
// arrange
final var capturedValues = new ArrayList<Integer>();
final var myObjectList = spy(new ArrayList<>());
doAnswer(invocation -> {
final var r = invocation.callRealMethod();
capturedValues.add((Integer) r);
return r;
})
.when(myObjectList)
.size();
// act
myObjectList.size();
myObjectList.add("one");
myObjectList.size();
myObjectList.add("two");
myObjectList.size();
// assert
assertEquals(3, capturedValues.size());
assertEquals("[0, 1, 2]", capturedValues.toString());
}