Mockito: doAnswer Vs thenReturn

124

Saya menggunakan Mockito untuk pengujian unit layanan nanti. Saya bingung ketika menggunakan doAnswervs thenReturn.

Adakah yang bisa membantu saya secara detail? Sejauh ini saya sudah mencobanya dengan thenReturn.

Rajkumar Thambu
sumber

Jawaban:

167

Anda harus menggunakan thenReturnatau doReturnjika Anda mengetahui nilai kembalian pada saat Anda memalsukan panggilan metode. Nilai yang ditentukan ini dikembalikan saat Anda menjalankan metode tiruan.

thenReturn(T value) Menetapkan nilai kembali yang akan dikembalikan saat metode dipanggil.

@Test
public void test_return() throws Exception {
    Dummy dummy = mock(Dummy.class);
    int returnValue = 5;

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenReturn(returnValue);
    doReturn(returnValue).when(dummy).stringLength("dummy");
}

Answer digunakan saat Anda perlu melakukan tindakan tambahan saat metode tiruan dipanggil, misalnya saat Anda perlu menghitung nilai hasil berdasarkan parameter panggilan metode ini.

Gunakan doAnswer()ketika Anda ingin menghentikan metode void dengan generik Answer.

Jawaban menentukan tindakan yang dijalankan dan nilai yang dikembalikan saat Anda berinteraksi dengan tiruan.

@Test
public void test_answer() throws Exception {
    Dummy dummy = mock(Dummy.class);
    Answer<Integer> answer = new Answer<Integer>() {
        public Integer answer(InvocationOnMock invocation) throws Throwable {
            String string = invocation.getArgumentAt(0, String.class);
            return string.length() * 2;
        }
    };

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenAnswer(answer);
    doAnswer(answer).when(dummy).stringLength("dummy");
}
Roland Weisleder
sumber
hai @Roland Weisleder tetapi kadang-kadang Anda harus mengembalikan beberapa nilai kode batin yang dihasilkan dan tidak ada hubungannya dengan argumen, misalnya code = UUID.randomUUID(), saya menemukan tidak mungkin untuk menerapkan ini dengan mockito.
zhuguowei
3
Saat tiruan Anda harus mengembalikan UUID baru untuk setiap pemanggilan, Anda akan menerapkan Answerjust with return UUID.randomUUID();.
Roland Weisleder
Dapatkah saya mengambil metode ini dari semut inisialisasi Jawaban baru, meletakkannya di beberapa metode, untuk membuat kode sedikit lebih bersih?
Jalur
3
@Line Answeradalah antarmuka fungsional, jadi dengan Java 8 Anda dapat menggantinya dengan ekspresi lambda. Jika tidak cukup bersih, pemfaktoran ulang biasa dan tidak biasa lainnya dimungkinkan.
Roland Weisleder
@ zhuguowei: mengembalikan beberapa nilai kode dalam yang dihasilkan? Bagaimana apanya?
Saurabh Patil
34

doAnswerdan thenReturnlakukan hal yang sama jika:

  1. Anda menggunakan Mock, bukan Spy
  2. Metode yang Anda hentikan mengembalikan nilai, bukan metode kosong.

Mari kita mengejek BookService ini

public interface BookService {
    String getAuthor();
    void queryBookTitle(BookServiceCallback callback);
}

Anda bisa menghentikan getAuthor () menggunakan doAnswerdan thenReturn.

BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return "Joshua";
    }
}).when(service).getAuthor();

Perhatikan bahwa saat menggunakan doAnswer, Anda tidak dapat meneruskan metode when.

// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());

Jadi, ketika Anda akan menggunakan doAnswerbukan thenReturn? Saya dapat memikirkan dua kasus penggunaan:

  1. Bila Anda ingin "stub" metode void.

Dengan menggunakan doAnswer, Anda dapat melakukan beberapa tindakan tambahan setelah pemanggilan metode. Misalnya, memicu panggilan balik di queryBookTitle.

BookServiceCallback callback = new BookServiceCallback() {
    @Override
    public void onSuccess(String bookTitle) {
        assertEquals("Effective Java", bookTitle);
    }
};
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
        callback.onSuccess("Effective Java");
        // return null because queryBookTitle is void
        return null;
    }
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
  1. Saat Anda menggunakan Spy, bukan Mock

Saat menggunakan when-thenReturn on Spy Mockito akan memanggil metode nyata dan kemudian menghentikan jawaban Anda. Ini dapat menyebabkan masalah jika Anda tidak ingin memanggil metode nyata, seperti dalam contoh ini:

List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));

Dengan menggunakan doAnswer, kami dapat menghentikannya dengan aman.

List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));

Sebenarnya, jika Anda tidak ingin melakukan tindakan tambahan setelah pemanggilan metode, Anda bisa menggunakan doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
aldok
sumber
bagaimana jika metode yang diejek tidak berlaku?
Igor Donin
1
Igor, di situlah doAnswer () muncul dan dia telah membahasnya dalam jawaban di atas.
Saurabh Patil
Saat menggunakan doAnswer(new Answer() { ... return null;}saya mendapatkan peringatan di gerhana untuk "Jawaban adalah tipe mentah. Referensi untuk tipe umum Jawaban <T> harus diparameterisasi". Apakah ada cara untuk menyelesaikan ini (kecuali mengabaikan peringatan c)?
LazR