Bagaimana cara memberitahu objek mockito mock untuk mengembalikan sesuatu yang berbeda saat dipanggil?


202

Jadi, saya membuat objek tiruan sebagai variabel statis di tingkat kelas seperti ... Di satu tes, saya ingin Foo.someMethod()mengembalikan nilai tertentu, sedangkan di tes lain, saya ingin mengembalikan nilai yang berbeda. Masalah yang saya alami adalah sepertinya saya perlu membangun kembali tiruan agar ini berfungsi dengan benar. Saya ingin menghindari membangun kembali tiruan, dan hanya menggunakan benda yang sama di setiap tes.

class TestClass {

    private static Foo mockFoo;

    @BeforeClass
    public static void setUp() {
        mockFoo = mock(Foo.class);
    }

    @Test
    public void test1() {
        when(mockFoo.someMethod()).thenReturn(0);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), receiving 0 as the value

    }

    @Test
    public void test2() {
        when(mockFoo.someMethod()).thenReturn(1);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), STILL receiving 0 as the value, instead of expected 1.

    }

}

Dalam tes kedua, saya masih menerima 0 sebagai nilai ketika testObj.bar () dipanggil ... Apa cara terbaik untuk menyelesaikan ini? Perhatikan bahwa saya tahu saya bisa menggunakan tiruan yang berbeda Foodalam setiap tes, namun, saya harus mengaitkan beberapa permintaan mockFoo, yang berarti saya harus melakukan chaining di setiap tes.

Jawaban:


43

Pertama-tama jangan membuat tiruannya statis. Jadikan bidang pribadi. Cukup masukkan kelas setUp Anda di @Beforenot @BeforeClass. Mungkin dijalankan banyak, tapi murah.

Kedua, cara Anda memilikinya sekarang adalah cara yang benar untuk mendapatkan tiruan untuk mengembalikan sesuatu yang berbeda tergantung pada tes.


439

Anda juga dapat Stub Panggilan Berturut-turut (# 10 dalam 2.8.9 api). Dalam hal ini, Anda akan menggunakan beberapa thenReturn panggilan atau satu thenReturn panggilan dengan beberapa parameter (varargs).

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class TestClass {

    private Foo mockFoo;

    @Before
    public void setup() {
        setupFoo();
    }

    @Test
    public void testFoo() {
        TestObject testObj = new TestObject(mockFoo);

        assertEquals(0, testObj.bar());
        assertEquals(1, testObj.bar());
        assertEquals(-1, testObj.bar());
        assertEquals(-1, testObj.bar());
    }

    private void setupFoo() {
        mockFoo = mock(Foo.class);

        when(mockFoo.someMethod())
            .thenReturn(0)
            .thenReturn(1)
            .thenReturn(-1); //any subsequent call will return -1

        // Or a bit shorter with varargs:
        when(mockFoo.someMethod())
            .thenReturn(0, 1, -1); //any subsequent call will return -1
    }
}

171
Saya pikir Anda juga dapat memanfaatkan fakta bahwa .thenReturn () membutuhkan varargs, sehingga kode dapat disingkat menjadi: when (mockFoo.someMethod ()). KemudianReturn (0, 1, -1);
Justin Muller

10
@JustinMuller - itu layak jawaban terpisah, saya pikir (berlawanan dengan komentar)
Brian Agnew

16
Jawaban ini bukan hal yang benar untuk dilakukan dalam kasus ini. Jika Anda mematikan metode ini untuk mengembalikan 0 dan 1, maka Anda akan baik-baik saja selama Anda menjalankan test1dan kemudian test2. Tetapi mungkin lingkungan integrasi berkelanjutan Anda akan menjalankan tes dalam urutan lain. Atau mungkin Anda ingin menjalankannya test2sendiri, tanpa berlaritest1 terlebih dahulu, dalam hal ini ia akan gagal. Tes unit harus selalu independen satu sama lain; dan tidak boleh ada ketergantungan antara tes individual, atau ketergantungan pada urutan tes tertentu. Sedangkan thenReturnpernyataan chaining ...
Dawood ibn Kareem

4
... memiliki kegunaannya, seperti halnya menggunakan varargs untuk satu thenReturn , itu bukan solusi yang benar dalam kasus khusus ini. Tampak bagi saya bahwa gerombolan pemberi suara di sini kemungkinan besar gagal untuk memahami pertanyaan itu.
Dawood ibn Kareem

2
Junit sendiri tidak memastikan pesanan tes tanpa @FixMethodOrder
Roger

29

Untuk semua yang mencari untuk mengembalikan sesuatu dan kemudian untuk pengecualian lemparan panggilan lain:

    when(mockFoo.someMethod())
            .thenReturn(obj1)
            .thenReturn(obj2)
            .thenThrow(new RuntimeException("Fail"));

atau

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));

19

Atau, bahkan lebih bersih:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);

2
Ini seharusnya jawabannya.
Ikthiander

14

Untuk Siapa saja yang menggunakan mata-mata () dan doReturn () alih-alih metode when ():

yang Anda perlukan untuk mengembalikan objek berbeda pada panggilan berbeda adalah ini:

doReturn(obj1).doReturn(obj2).when(this.spyFoo).someMethod();

.

Untuk mock klasik:

when(this.mockFoo.someMethod()).thenReturn(obj1, obj2);

atau dengan pengecualian yang dilemparkan:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
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.