Bisakah Mockito menangkap argumen dari metode yang disebut beberapa kali?

446

Saya memiliki metode yang dipanggil dua kali, dan saya ingin menangkap argumen panggilan metode kedua.

Inilah yang saya coba:

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

Tapi saya mendapatkan TooManyActualInvocationspengecualian, karena Mockito berpikir itu doSomethinghanya boleh disebut sekali.

Bagaimana saya bisa memverifikasi argumen panggilan kedua doSomething?

Eric Wilson
sumber

Jawaban:

784

Saya pikir seharusnya begitu

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

Contoh dari 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());
proactif
sumber
3
Bisakah Anda menangkap argumen yang diteruskan doSomething()dalam setiap doa terpisah dengan ini?
matt b
36
Perlu dicatat bahwa jika Anda melakukan sesuatu seperti ini: Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);argumen yang ditangkap akan sama dua kali (karena sebenarnya itu adalah objek orang yang sama), jadi capturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane", lihat juga groups.google.com/forum/#!msg/mockito/ KBRocVedYT0 / 5HtARMl9r2wJ .
asmaier
2
Ini bagus, tetapi bagaimana saya bisa menguji dua pemanggilan objek yang diketik berbeda? Misalnya ExecutorService.submit (MyRunableImpl baru ()); dan kemudian ExecutorService.submit (MyAnotherRunableImpl () baru)?
Leon
Jika seseorang perlu menangani kasus yang dijelaskan oleh @asmaier, saya mengirim jawaban di sini: stackoverflow.com/a/36574817/1466267
SpaceTrucker
1
Bagi siapa pun yang masih bertanya-tanya tentang jawaban untuk pertanyaan Leon, Anda akan menggunakan kelas dasar umum ( Runnable) dan, jika perlu, lakukan pemeriksaan jenis yang lebih spesifik pada argumen yang ditangkap.
Matius Baca
50

Sejak Mockito 2.0 ada juga kemungkinan menggunakan metode statis Matchers.argThat (ArgumentMatcher) . Dengan bantuan Java 8 sekarang jauh lebih bersih dan lebih mudah dibaca untuk ditulis:

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

Jika Anda terikat ke versi Java yang lebih rendah, ada juga yang tidak terlalu buruk:

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

Tentu saja tidak satu pun dari mereka yang dapat memverifikasi urutan panggilan - yang harus Anda gunakan 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")));

Silakan lihat proyek mockito-java8 yang memungkinkan untuk melakukan panggilan seperti:

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));
Maciej Dobrowolski
sumber
2
Ini adalah teknik yang bagus. Saya saat ini mendapatkan beberapa output yang agak samar: "Dicari tetapi tidak dipanggil: / n mockAppender.append (<Manajer indeks ut $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>);" - Arg ada CharSequence. Apakah Anda tahu cara untuk mendapatkan laporan untuk mencetak argumen "yang diinginkan" dengan benar?
mike rodent
@mikerodent Output cryptic dapat diperbaiki jika Anda menggunakan rute yang lebih jelas untuk membuat kelas yang mengimplementasikan ArgumentMatcher <T>. Mengganti metode toString dalam implementasi Anda akan memberikan pesan apa pun yang Anda inginkan dalam output uji mockito.
Noah Solomon
25

Jika Anda tidak ingin memvalidasi semua panggilan doSomething(), hanya yang terakhir, Anda bisa menggunakannya ArgumentCaptor.getValue(). Menurut javadoc Mockito :

Jika metode ini dipanggil beberapa kali maka itu mengembalikan nilai yang diambil terbaru

Jadi ini akan berhasil (anggap Foomemiliki metode 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());
lreeder
sumber
apakah ada cara untuk menangkap kedua nilai tersebut?
Hars
9

Anda juga dapat menggunakan @Captor ArgumentCaptor beranotasi. Sebagai contoh:

@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));
}
Michał Stochmal
sumber
6

Dengan lambdas Java 8, cara yang mudah adalah menggunakan

org.mockito.invocation.InvocationOnMock

when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
    assertEquals("myCollection", invocationOnMock.getArgument(0));
    assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}
Anton Seredkin
sumber
Saya tidak dapat melihat bagaimana ini lebih nyaman daripada cara lama. Saya suka sekali menggunakan lambdas, tapi saya tidak yakin apakah ini salah satu.
Eric Wilson
0

Pertama-tama: Anda harus selalu mengimpor mockito statis, dengan cara ini kode akan jauh lebih mudah dibaca (dan intuitif) - contoh kode di bawah ini mengharuskannya berfungsi:

import static org.mockito.Mockito.*;

Dalam metode verifikasi () Anda bisa melewati ArgumentCaptor untuk memastikan eksekusi dalam tes dan ArgumentCaptor untuk mengevaluasi argumen:

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

Daftar semua argumen yang diteruskan selama pengujian Anda dapat diakses melalui metode argument.getAllValues ​​().

Nilai argumen tunggal (terakhir disebut) dapat diakses melalui argument.getValue () untuk manipulasi / pemeriksaan lebih lanjut atau apa pun yang ingin Anda lakukan.

fl0w
sumber