Menggunakan Mockito dengan beberapa panggilan ke metode yang sama dengan argumen yang sama

289

Apakah ada cara agar metode stubbed mengembalikan objek yang berbeda pada pemanggilan berikutnya? Saya ingin melakukan ini untuk menguji tanggapan yang tidak ditentukan dari ExecutorCompletionService. yaitu untuk menguji bahwa terlepas dari urutan pengembalian metode, hasilnya tetap konstan.

Kode yang ingin saya uji terlihat seperti ini.

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

Jawaban:

254

Anda dapat melakukannya menggunakan thenAnswermetode (saat merantai dengan when):

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

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

        return 2;
    }
});

Atau menggunakan doAnswermetode statis yang setara :

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

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

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

Bagaimana tentang

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

Anda dapat menempatkan argumen sebanyak yang Anda suka di kurung thenReturn, asalkan semuanya adalah tipe yang benar. Nilai pertama akan dikembalikan saat metode pertama dipanggil, lalu jawaban kedua, dan seterusnya. Nilai terakhir akan dikembalikan berulang kali setelah semua nilai lainnya habis.

Dawood ibn Kareem
sumber
4
Ini akan bekerja dengan tiruan, tetapi tidak dengan mata-mata. Jika Anda perlu mencegah memanggil metode asli, Anda perlu doAnswer (...). When (someSpy) .someMethod (...).
Yuri
6
@ Yuri - tidak cukup. Anda tidak perlu doAnsweratau untuk menulis Answerdalam kasus yang Anda sebutkan. Anda bisa menggunakannya doReturn(...).when(someSpy).someMethod(...). Tampaknya masuk akal untuk berasumsi bahwa Emma lebih tertarik pada ejekan, daripada mata-mata, tetapi saya rasa saya bisa menambahkan sesuatu ke jawaban saya untuk menguraikan ini. Terima kasih atas komentarnya.
Dawood ibn Kareem
@ DawoodibnKareem katakanlah untuk panggilan pertama saya ingin mengembalikan nilai dan untuk panggilan kedua saya ingin membuang Pengecualian. Bagaimana ini bisa dilakukan?
Rito
@Rito Harap baca jawaban Volodymyr atau Raystorm. Mereka berdua membahas kasus itu.
Dawood ibn Kareem
Jawaban yang sangat mulia.
wild_nothing
151

Seperti yang ditunjukkan sebelumnya, hampir semua panggilan dapat dilakukan dengan rantai.

Jadi kamu bisa menelepon

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

Info lebih lanjut di Dokumentasi Mockito .

Badai badai
sumber
3
Sangat membantu! Apa yang akan terjadi ketika keempat kalinya mock.methoddipanggil dalam contoh ini? Saya menginginkan sesuatu seperti, kembalikan foo pertama kali tetapi kembalikan bar untuk SEMUA sisanya.
javaPlease42
6
Setiap doa tambahan pada mock akan mengembalikan 'thenReturn' yang terakhir atau yang terakhir 'thenThrow' Sangat berguna
Francois Lacoursiere
Terima kasih atas instruksi yang bagus dan sederhana. Tidak pernah tahu ini sampai sekarang. Saya kesulitan menemukan cara mendapatkan kembali dua hasil berbeda pada dua panggilan yang identik. Hemat saya banyak waktu.
CharlesC
68

Anda bahkan dapat doReturn()mengaitkan pemanggilan metode seperti ini

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

imut bukan :)

Volodymyr Kozubal
sumber
4

Saya telah menerapkan MultipleAnswerkelas yang membantu saya untuk mematikan jawaban yang berbeda dalam setiap panggilan. Berikut potongan kode:

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);
    }
}
victorvmp
sumber
1
Bisakah Anda menginisialisasi objek itu dengan cara yang singkat, sederhana dan mudah dibaca?
usr-local-ΕΨΗΕΛΩΝ
1

Mengikuti dapat digunakan sebagai metode umum untuk mengembalikan argumen yang berbeda pada panggilan metode yang berbeda. Satu-satunya hal yang perlu kita lakukan adalah kita harus melewati array dengan urutan objek mana yang harus diambil dalam setiap panggilan.

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

Ex. getAnswerForSubsequentCalls(mock1, mock3, mock2);akan mengembalikan objek mock1 pada panggilan pertama, objek mock3 pada panggilan kedua dan objek mock2 pada panggilan ketiga. Harus digunakan seperti when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); ini hampir mirip denganwhen(something()).thenReturn(mock1, mock3, mock2);

yuva 443
sumber
1

Terkait dengan jawaban @ [Igor Nikolaev] dari 8 tahun yang lalu, menggunakan Answercan dapat disederhanakan menggunakan ekspresi lambda yang tersedia di Java 8.

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

atau lebih sederhana:

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

Gaya BDD:

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

doReturn (value1, value2, value3) .when (metode-panggilan)

disempurnakanJack
sumber