Contoh argumentCaptor Mockito

152

Adakah yang bisa memberi saya contoh yang menunjukkan cara menggunakan org.mockito.ArgumentCaptorkelas dan perbedaannya dari pencocokan sederhana yang disediakan dengan mockito.

Saya membaca dokumen mockito yang disediakan tetapi itu tidak menggambarkannya dengan jelas, tidak ada yang bisa menjelaskannya dengan jelas.

Ujjwal
sumber

Jawaban:

203

Saya setuju dengan apa yang dikatakan @fge, lebih dari itu. Mari kita lihat contohnya. Pertimbangkan Anda memiliki metode:

class A {
    public void foo(OtherClass other) {
        SomeData data = new SomeData("Some inner data");
        other.doSomething(data);
    }
}

Sekarang jika Anda ingin memeriksa data dalam Anda dapat menggunakan penculik:

// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);

// Run the foo method with the mock
new A().foo(other);

// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());

// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);
Slava Shpitalny
sumber
Jika doSomething(data)bermutasi innerData, apakah perubahan itu akan ada assertEquals("Some inner data", actual.innerData), atau akan innerDataditangkap apa adanya sebelum doSomething dieksekusi?
Cory Klein
@CoryKlein Ini OtherClassadalah tiruan, dan seperti yang didefinisikan sekarang, doSomething()sebenarnya tidak akan melakukan apa pun, itu hanya merekam objek yang telah lewat. Artinya akan ditangkap apa adanya sebelum doSomethingdieksekusi.
Slava Shpitalny
3
Dalam verify, times(1)adalah default dan dapat dihilangkan.
Inego
bagaimana ArgumentCaptor tahu bahwa foo (other) terjadi karena hanya dipakai setelah panggilan foo (other)?
AvramPop
2
@AvramPop orang yang tahu ini adalah objek tiruan. Ini berisi di dalam banyak informasi tentang tiruan. Di dalam semua informasi itu juga berisi riwayat panggilan untuk setiap metode dengan parameternya. Saat Anda memanggil verifymetode, ia menggunakan informasi itu untuk menjalankan kecocokan terhadap verifikasi yang Anda lakukan. Untuk setiap parameter ia menanyakan apakah cocok dengan panggilan spesifik yang diperiksa. Saat ArgumentCaptor dicentang, ia hanya menyimpan nilai yang dipanggil dengannya sehingga saat verifydiakhiri, ia menampung semua pemanggilan yang relevan. Ini kira-kira cara kerjanya. Semoga membantu
Slava Shpitalny
37

Dua perbedaan utama adalah:

  • ketika Anda menangkap bahkan satu argumen, Anda dapat melakukan pengujian yang jauh lebih rumit pada argumen ini, dan dengan kode yang lebih jelas;
  • sebuah ArgumentCaptorkaleng menangkap lebih dari sekali.

Untuk mengilustrasikan yang terakhir, katakanlah Anda memiliki:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(x, times(4)).someMethod(captor.capture()); // for instance

Kemudian penculik akan dapat memberi Anda akses ke semua 4 argumen, yang kemudian Anda dapat melakukan pernyataan secara terpisah.

Sebenarnya ini atau sejumlah argumen, karena a VerificationModetidak terbatas pada sejumlah pemanggilan tetap; Bagaimanapun, penculik akan memberi Anda akses ke semuanya, jika Anda mau.

Ini juga memiliki keuntungan bahwa pengujian semacam itu (imho) jauh lebih mudah untuk ditulis daripada harus mengimplementasikan ArgumentMatchers Anda sendiri - terutama jika Anda menggabungkan mockito dengan assertj.

Oh, dan mohon pertimbangkan untuk menggunakan TestNG daripada JUnit.

fge
sumber
1
Bagaimana jika ada beberapa parameter yang diteruskan ke metode - semua jenis berbeda? Bagaimana Anda benar-benar memverifikasi bahwa parameter boolean benar , misalnya.
IgorGanapolsky
24
Bisakah Anda memberikan penjelasan untuk komentar Anda: Oh, dan mohon pertimbangkan untuk menggunakan TestNG daripada JUnit. . Mengapa mempertimbangkannya? Mengapa berubah?
Navigatron
1
@IgorGanapolsky Anda baru saja menambahkan ArgumentCaptor lain. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Michael michael = Michael baru (); michael.sayHi (j); verifikasi (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("nilai adalah" + arg.getValue ()); System.out.println ("string adalah" + arg2.getValue ());
johnwick0831
15

Langkah-langkah untuk melakukan pemeriksaan lengkap adalah:

Siapkan penculik:

ArgumentCaptor<SomeArgumentClass> someArgumentCaptor = ArgumentCaptor.forClass(SomeArgumentClass.class);

verifikasi panggilan untuk bergantung pada komponen (kolaborator subjek yang sedang diuji) kali (1), adalah nilai default, jadi ne perlu menambahkannya.

verify(dependentOnComponent, times(1)).send(someArgumentCaptor.capture());

times (1) adalah defaultnya, jadi tidak perlu menambahkannya.

Dapatkan argumen diteruskan ke kolaborator

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument bisa digunakan untuk pernyataan

Lho Ben
sumber
-2

Di sini saya memberi Anda contoh yang tepat dari satu metode panggilan balik. jadi misalkan kita memiliki metode seperti metode login ():

 public void login() {
    loginService = new LoginService();
    loginService.login(loginProvider, new LoginListener() {
        @Override
        public void onLoginSuccess() {
            loginService.getresult(true);
        }

        @Override
        public void onLoginFaliure() {
            loginService.getresult(false);

        }
    });
    System.out.print("@@##### get called");
}

Saya juga meletakkan semua kelas helper di sini untuk membuat contoh lebih jelas: kelas loginService

public class LoginService implements Login.getresult{
public void login(LoginProvider loginProvider,LoginListener callback){

    String username  = loginProvider.getUsername();
    String pwd  = loginProvider.getPassword();
    if(username != null && pwd != null){
        callback.onLoginSuccess();
    }else{
        callback.onLoginFaliure();
    }

}

@Override
public void getresult(boolean value) {
    System.out.print("login success"+value);
}}

dan kami memiliki pendengar LoginListener sebagai:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

sekarang saya hanya ingin menguji metode login () dari kelas Login

 @Test
public void loginTest() throws Exception {
    LoginService service = mock(LoginService.class);
    LoginProvider provider = mock(LoginProvider.class);
    whenNew(LoginProvider.class).withNoArguments().thenReturn(provider);
    whenNew(LoginService.class).withNoArguments().thenReturn(service);
    when(provider.getPassword()).thenReturn("pwd");
    when(provider.getUsername()).thenReturn("username");
    login.getLoginDetail("username","password");

    verify(provider).setPassword("password");
    verify(provider).setUsername("username");

    verify(service).login(eq(provider),captor.capture());

    LoginListener listener = captor.getValue();

    listener.onLoginSuccess();

    verify(service).getresult(true);

juga jangan lupa untuk menambahkan anotasi di atas kelas pengujian sebagai

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)
Vikram singh
sumber
1
Bukankah seharusnya itu merujuk ke ArgumentCaptor?
Felipe Martins Melo
ya kami menangkap pendengar yang diteruskan ke metode login () dalam contoh login (LoginProvider loginProvider, panggilan balik LoginListener)
Vikram singh
Di manakah yang captordidefinisikan dalam jawaban Anda?
tom_mai78101
ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh