Mockitoは、複数回呼び出されたメソッドの引数をキャプチャできますか?


446

2回呼び出されるメソッドがあり、2番目のメソッド呼び出しの引数をキャプチャしたい。

これが私が試したものです:

ArgumentCaptor<Foo> firstFooCaptor = ArgumentCaptor.forClass(Foo.class);
ArgumentCaptor<Foo> secondFooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar).doSomething(firstFooCaptor.capture());
verify(mockBar).doSomething(secondFooCaptor.capture());
// then do some assertions on secondFooCaptor.getValue()

しかしTooManyActualInvocations、MockitoはそれdoSomethingを1回だけ呼び出す必要があると考えているため、例外が発生します。

の2番目の呼び出しの引数を確認するにはどうすればよいdoSomethingですか?

回答:


783

そうだと思う

verify(mockBar, times(2)).doSomething(...)

mockito javadocのサンプル:

ArgumentCaptor<Person> peopleCaptor = ArgumentCaptor.forClass(Person.class);
verify(mock, times(2)).doSomething(peopleCaptor.capture());

List<Person> capturedPeople = peopleCaptor.getAllValues();
assertEquals("John", capturedPeople.get(0).getName());
assertEquals("Jane", capturedPeople.get(1).getName());

3
doSomething()これで、各個別の呼び出しで渡された引数をキャプチャできますか?
マットb

36
次のような場合 Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);、キャプチャされた引数は2回同じになります(実際には同じ人物オブジェクトであるため)。そのためcapturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane"groups.google.com / forum /#!msg / mockito /も参照してください。 KBRocVedYT0 / 5HtARMl9r2wJ
asmaier 2014

2
これはいいですが、2つの異なる型のオブジェクト呼び出しをどのようにテストできますか?たとえば、ExecutorService.submit(new MyRunableImpl()); 次にExecutorService.submit(new MyAnotherRunableImpl())?
Leon

@asmaierで説明されているケースを処理する必要がある場合、私はここに回答を投稿しました:stackoverflow.com/a/36574817/1466267
SpaceTrucker

1
レオンの質問への回答についてまだ疑問に思っている人は、共通の基本クラス(Runnable)を使用し、必要に応じて、キャプチャされた引数に対してより具体的な型チェックを実行します。
Matthewが

50

Mockito 2.0以降、静的メソッドMatchers.argThat(ArgumentMatcher)を使用する可能性もあります。Java 8の助けを借りて、今ではよりクリーンで読みやすくなっています。

verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));

あなたがより低いJavaバージョンに縛られているなら、それも悪くはありません:

verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
        @Override
        public boolean matches(Object emp) {
            return ((Employee) emp).getSurname().equals("SomeSurname");
        }
    }));

もちろん、これらのどれも呼び出しの順序を確認できません-InOrderを使用する必要があります:

InOrder inOrder = inOrder(mockBar);

inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));

次のような呼び出しを可能にするmockito-java8プロジェクトをご覧ください。

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));

2
これは素晴らしいテクニックです。現在、かなりわかりにくい出力が表示されています。「必要ですが、呼び出されていません:/ n mockAppender.append(<Index manager ut $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>);」-引数がありますCharSequence。レポートに「必要な」引数を適切に出力させる方法を知っていますか?
マイクげっ歯類

@mikerodent ArgumentMatcher <T>を実装するクラスを作成する、より冗長な方法を使用する場合、不可解な出力を修正できます。実装でtoStringメソッドをオーバーライドすると、mockitoテスト出力に必要なメッセージが表示されます。
ノアソロモン

25

へのすべての呼び出しを検証したくない場合はdoSomething()、最後の呼び出しのみを検証し、そのまま使用できますArgumentCaptor.getValue()Mockito javadocによると:

メソッドが複数回呼び出された場合、最新のキャプチャされた値を返します

したがって、これは機能します(Fooメソッドがあると仮定しますgetName()):

ArgumentCaptor<Foo> fooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar, times(2)).doSomething(fooCaptor.capture());
//getValue() contains value set in second call to doSomething()
assertEquals("2nd one", fooCaptor.getValue().getName());

両方の値を取得する方法はありますか?
Hars

9

@Captor注釈付きArgumentCaptorを使用することもできます。例えば:

@Mock
List<String> mockedList;

@Captor
ArgumentCaptor<String> argCaptor;

@BeforeTest
public void init() {
    //Initialize objects annotated with @Mock, @Captor and @Spy.
    MockitoAnnotations.initMocks(this);
}

@Test
public void shouldCallAddMethodTwice() {
    mockedList.add("one");
    mockedList.add("two");
    Mockito.verify(mockedList, times(2)).add(argCaptor.capture());

    assertEquals("one", argCaptor.getAllValues().get(0));
    assertEquals("two", argCaptor.getAllValues().get(1));
}

6

Java 8のラムダでは、便利な方法は

org.mockito.invocation.InvocationOnMock

when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
    assertEquals("myCollection", invocationOnMock.getArgument(0));
    assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}

これが従来の方法よりも便利な方法を私は見ることができません。私はラムダを上手に使うのが大好きですが、これがそうであるかどうかはわかりません。
エリックウィルソン

0

まず、常にmockito staticをインポートする必要があります。これにより、コードがより読みやすく(直感的に)なります。

import static org.mockito.Mockito.*;

verify()メソッドでは、テストでの実行を保証するArgumentCaptorと、引数を評価するArgumentCaptorを渡すことができます。

ArgumentCaptor<MyExampleClass> argument = ArgumentCaptor.forClass(MyExampleClass.class);
verify(yourmock, atleast(2)).myMethod(argument.capture());

List<MyExampleClass> passedArguments = argument.getAllValues();

for (MyExampleClass data : passedArguments){
    //assertSometing ...
    System.out.println(data.getFoo());
}

テスト中に渡されたすべての引数のリストには、argument.getAllValues()メソッドを介してアクセスできます。

単一の(最後に呼び出された)引数の値は、argument.getValue()を介してアクセスでき、さらに操作/チェックしたり、何でもしたいことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.