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?

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

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

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

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

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:"));
}

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.