web-dev-qa-db-ja.com

同じ引数で同じメソッドを複数回呼び出してMockitoを使う

スタブ化されたメソッドがその後の呼び出しで異なるオブジェクトを返すようにする方法はありますか? ExecutorCompletionServiceからの未確定のレスポンスをテストするためにこれを行いたいです。つまり、メソッドの戻り順に関係なくテストするために、結果は一定のままです。

私がテストしようとしているコードはこのようなものです。

// 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 (...) { }        
}
229
Emma

thenAnswer メソッドを使用してそれを行うことができます( when とチェーンする場合)。

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

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

        return 2;
    }
});

または、同等の静的 doAnswer メソッドを使用します。

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

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

        return 2;
    }
}).when(someMock).someMethod();
202
Igor Nikolaev

どうですか?

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

すべて正しい型であれば、thenReturnの括弧内に好きなだけ引数を入れることができます。最初の値は、メソッドが最初に呼び出されたときに返され、次に2番目の値が返されます。他のすべての値が使い果たされると、最後の値が繰り返し返されます。

533

As 以前に指摘したように ほとんどすべての呼び出しは連鎖可能です。

だからあなたは呼び出すことができます

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

Mockito's Documenation に詳しい情報があります。

108
Raystorm

このようにdoReturn()メソッドの呼び出しを連鎖することさえ可能です

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

かわいいじゃないか:)

60

私はMultipleAnswerクラスを実装しました。これは私があらゆる呼び出しで異なる答えをスタブするのを助けます。ここでのコードの一部:

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

以下は、異なるメソッド呼び出しで異なる引数を返すための共通メソッドとして使用できます。必要なことは、各呼び出しでオブジェクトを取得する順序で配列を渡す必要があることだけです。

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

getAnswerForSubsequentCalls(mock1, mock3, mock2);は最初の呼び出しでmock1オブジェクトを、二番目の呼び出しでmock3オブジェクトを、三番目の呼び出しでmock2オブジェクトを返します。 when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2));のように使うべきですこれはwhen(something()).thenReturn(mock1, mock3, mock2);とほとんど同じです

1
yuva 443

8年前の@ [Igor Nikolaev]の回答に関連して、Answerを使用することは、Java 8で利用可能な ラムダ式 を使用していくらか単純化することができます。

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

もっと簡単に言うと:

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

BDDスタイル:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);
0
epox