简体   繁体   English

使用 Mockito 多次调用具有相同参数的相同方法

[英]Using Mockito with multiple calls to the same method with the same arguments

Is there a way to have a stubbed method return different objects on subsequent invocations?有没有办法让存根方法在后续调用中返回不同的对象? I'd like to do this to test nondeterminate responses from an ExecutorCompletionService .我想这样做是为了测试来自ExecutorCompletionService不确定响应。 ie to test that irrespective of the return order of the methods, the outcome remains constant.即测试无论方法的返回顺序如何,结果都保持不变。

The code I'm looking to test looks something like this.我要测试的代码看起来像这样。

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}

How about怎么样

when( method-call ).thenReturn( value1, value2, value3 );

You can put as many arguments as you like in the brackets of thenReturn, provided they're all the correct type.您可以在 thenReturn 的括号中放入任意数量的参数,前提是它们都是正确的类型。 The first value will be returned the first time the method is called, then the second answer, and so on.第一次调用该方法时将返回第一个值,然后是第二个答案,依此类推。 The last value will be returned repeatedly once all the other values are used up.一旦所有其他值都用完,将重复返回最后一个值。

You can do that using the thenAnswer method (when chaining with when ):您可以使用thenAnswer方法(当与when链接when )做到这一点:

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

Or using the equivalent, static doAnswer method:或者使用等效的静态doAnswer方法:

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();

As previously pointed out almost all of the calls are chainable.如前所述几乎所有的调用都是可链接的。

So you could call所以你可以打电话

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

More info in Mockito's Documenation . Mockito 的文档中的更多信息。

You can even chain doReturn() method invocations like this你甚至可以像这样链接doReturn()方法调用

doReturn(null).doReturn(anotherInstance).when(mock).method();

cute isn't it :)可爱不是吗:)

BDD style: BDD风格:

import static org.mockito.BDDMockito.given;
        ...

        given(yourMock.yourMethod()).willReturn(1, 2, 3);

Classic style:经典款式:

import static org.mockito.Mockito.when;
        ...

        when(yourMock.yourMethod()).thenReturn(1, 2, 3);

Explicit style:显式风格:

        ...

        when(yourMock.yourMethod())
            .thenReturn(1)
            .thenReturn(2)
            .thenReturn(3);

I've implemented a MultipleAnswer class that helps me to stub different answers in every call.我已经实现了一个MultipleAnswer类,它可以帮助我在每次通话中存根不同的答案。 Here the piece of code:这里是一段代码:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}

doReturn( value1, value2, value3 ).when(方法调用)

Related to @[Igor Nikolaev]'s answer from 8 years ago, using an Answer can be simplified somewhat using a lambda expression available in Java 8.与 8 年前@[Igor Nikolaev] 的回答相关,可以使用 Java 8 中提供的lambda 表达式稍微简化使用Answer

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

or more simply:或更简单地说:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());

Following can be used as a common method to return different arguments on different method calls.以下可以用作在不同方法调用上返回不同参数的常用方法。 Only thing we need to do is we need to pass an array with order in which objects should be retrieved in each call.我们唯一需要做的就是传递一个数组,其中包含在每次调用中应该检索对象的顺序。

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

Ex.前任。 getAnswerForSubsequentCalls(mock1, mock3, mock2); will return mock1 object on first call, mock3 object on second call and mock2 object on third call.将在第一次调用时返回 mock1 对象,在第二次调用时返回 mock3 对象,在第三次调用时返回 mock2 对象。 Should be used like when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2));应该像when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); This is almost similar to when(something()).thenReturn(mock1, mock3, mock2);这几乎类似于when(something()).thenReturn(mock1, mock3, mock2);

这是 BDD 风格的工作示例,非常简单明了

given(carRepository.findByName(any(String.class))).willReturn(Optional.empty()).willReturn(Optional.of(MockData.createCarEntity()));

You can use a LinkedList and an Answer .您可以使用LinkedListAnswer Eg例如

MyService mock = mock(MyService.class);
LinkedList<String> results = new LinkedList<>(List.of("A", "B", "C"));
when(mock.doSomething(any())).thenAnswer(invocation -> results.removeFirst());

This is not directly related to the question.这与问题没有直接关系。 But wanted to put this in the same chain.但想把它放在同一个链中。

If trying to verify the same method call with multiple arguments, you can use the below times feature by Mockito.如果尝试使用多个参数验证相同的方法调用,您可以使用 Mockito 的以下时间功能。 You don't need it if you are not verifying.如果您不进行验证,则不需要它。

Mockito.verify(method, times(n)).methoscall(); Mockito.verify(method, times(n)).methoscall();

Here is 'n' is the number of times the mock is invoked.这里的“n”是调用模拟的次数。

If you have a dynamic list of values you can use AdditionalAnswers.returnsElementsOf :如果您有一个动态值列表,您可以使用AdditionalAnswers.returnsElementsOf

import org.mockito.AdditionalAnswers;

when(mock.method()).thenAnswer(AdditionalAnswers.returnsElementsOf(myListOfValues));

This might be basic/obvious, but if like me you are trying to mock multiple calls for a method that is called unknown number of times per call to method to be tested, for example:这可能是基本的/显而易见的,但是如果像我一样,您尝试模拟多次调用一个方法,该方法每次调用要测试的方法调用次数未知,例如:

public String method(String testArg) {
    //...
    while(condition) {
        someValue = someBean.nestedMethod(); // This is called unknown number of times
        //...
    }
    //...
}

You can do something like:您可以执行以下操作:

@Test
public void testMethod() {
    mockNestedMethodForValue("value1");
    assertEquals(method("arg"), "expected1");
    mockNestedMethodForValue("value2");
    assertEquals(method("arg"), "expected2");
    mockNestedMethodForValue("value3");
    assertEquals(method("arg"), "expected3");
}

private void mockNestedMethodForValue(String value) {
    doReturn(value).when(someBeanMock).nestedMethod();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM