Mockito:doAnswer与thenReturn


123

我正在使用Mockito进行服务稍后的单元测试。我doAnswer对何时使用vs 感到困惑thenReturn

谁能详细帮助我?到目前为止,我已经尝试过使用thenReturn

Answers:


166

在模拟方法调用时,应使用thenReturndoReturn当您知道返回值时。调用模拟方法时,将返回此定义的值。

thenReturn(T value) 设置在调用该方法时要返回的返回值。

@Test
public void test_return() throws Exception {
    Dummy dummy = mock(Dummy.class);
    int returnValue = 5;

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenReturn(returnValue);
    doReturn(returnValue).when(dummy).stringLength("dummy");
}

Answer 当调用模拟方法时,例如,当您需要基于此方法调用的参数计算返回值时,如果需要执行其他操作,则使用。

doAnswer()当您想用generic对无效方法进行存根时使用Answer

答案指定执行的操作以及与模拟互动时返回的返回值。

@Test
public void test_answer() throws Exception {
    Dummy dummy = mock(Dummy.class);
    Answer<Integer> answer = new Answer<Integer>() {
        public Integer answer(InvocationOnMock invocation) throws Throwable {
            String string = invocation.getArgumentAt(0, String.class);
            return string.length() * 2;
        }
    };

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenAnswer(answer);
    doAnswer(answer).when(dummy).stringLength("dummy");
}

@Roland Weisleder,您好,但有时您应该返回一些值生成的内部代码,而与参数无关,例如code = UUID.randomUUID(),我发现无法使用来实现mockito
zhuguowei

3
当您的模拟应为每次调用返回新的UUID时,您将使用实现Answerjust return UUID.randomUUID();
罗兰·韦斯勒德

我可以从新的Answer初始化蚂蚁中将这种方法放入某种方法中,以使代码更简洁吗?

3
@Line Answer是一个功能接口,因此在Java 8中,您可以将其替换为lambda表达式。如果不够干净,则可以进行其他任何常见的异常重构。
罗兰·韦斯勒德

// @ zhuguowei:返回一些值生成的内部代码?你是什​​么意思?
萨拉·帕蒂(Saurabh Patil)'18

34

doAnswerthenReturn在以下情况下执行相同的操作:

  1. 您正在使用模拟,而不是间谍
  2. 您正在存根的方法将返回一个值,而不是void方法。

让我们来模拟这个BookService

public interface BookService {
    String getAuthor();
    void queryBookTitle(BookServiceCallback callback);
}

您可以使用doAnswer和存根getAuthor()thenReturn

BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return "Joshua";
    }
}).when(service).getAuthor();

请注意,使用时doAnswer,您无法在上传递方法when

// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());

所以,什么时候用doAnswer代替thenReturn?我可以想到两个用例:

  1. 当您想“存根” void方法时。

使用doAnswer,您可以在方法调用时执行一些附加操作。例如,在queryBookTitle上触发回调。

BookServiceCallback callback = new BookServiceCallback() {
    @Override
    public void onSuccess(String bookTitle) {
        assertEquals("Effective Java", bookTitle);
    }
};
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
        callback.onSuccess("Effective Java");
        // return null because queryBookTitle is void
        return null;
    }
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
  1. 当您使用间谍而不是模拟

当使用when-thenReturn时,Spy Mockito将调用真实方法,然后将您的答案存根。如果您不想像下面的示例那样调用真实方法,则可能会导致问题:

List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));

使用doAnswer,我们可以安全地将其存根。

List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));

实际上,如果您不想在方法调用时执行其他操作,则可以使用doReturn

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));

如果模拟方法无效,该怎么办?
伊戈尔·多宁

1
Igor,这正是doAnswer()出现的地方,他已经在上面的答案中进行了介绍。
萨拉·帕蒂(Saurabh Patil)'18年

使用时,doAnswer(new Answer() { ... return null;}我在eclipse中收到一条警告:“答案是原始类型。应该对通用类型Answer <T>的引用进行参数设置”。有没有办法解决这个问题(除了忽略c的警告)?
LazR
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.