Mockito: Metode Stubbing Yang Mengembalikan Jenis Dengan Kartu Liar Berbatas

135

Pertimbangkan kode ini:

public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
public class DummyClassTest {
    public void testMockitoWithGenerics() {
        DummyClass dummyClass = Mockito.mock(DummyClass.class);
        List<? extends Number> someList = new ArrayList<Integer>();
        Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
    }
}

Kompilator mengeluh tentang garis yang mencoba untuk mematikan perilaku dummyMethod() . Adakah petunjuk tentang bagaimana seseorang tentang metode stubbing yang mengembalikan tipe dengan wild-card yang dibatasi?

Shikhar Mishra
sumber
Bisakah Anda memperbarui potongan kode untuk menampilkan tipe generik?
millhouse
1
Selesai Saya harus menghapus tag pra dan kode, mereka menanggalkan <? extends Number> dari deklarasi tipe.
Shikhar Mishra

Jawaban:

190

Anda juga dapat menggunakan metode aman non-tipe doReturn untuk tujuan ini,

@Test
public void testMockitoWithGenerics()
{
    DummyClass dummyClass = Mockito.mock(DummyClass.class);
    List<? extends Number> someList = new ArrayList<Integer>();

    Mockito.doReturn(someList).when(dummyClass).dummyMethod();

    Assert.assertEquals(someList, dummyClass.dummyMethod());
}

seperti yang dibahas di grup google Mockito.

Meskipun ini lebih sederhana daripada thenAnswer, sekali lagi perhatikan bahwa ini bukan tipe yang aman. Jika Anda khawatir tentang keamanan jenis, jawaban millhouse sudah benar.

detil tambahan

Agar lebih jelas, inilah kesalahan kompiler yang diamati,

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

Saya percaya kompiler telah menetapkan tipe wildcard pertama selama whenpanggilan dan kemudian tidak dapat mengkonfirmasi bahwa tipe wildcard kedua dalam thenReturnpanggilan adalah sama.

Sepertinya thenAnswertidak mengalami masalah ini karena ia menerima tipe wildcard saat thenReturnmengambil tipe non-wildcard, yang harus ditangkap. Dari Mockito's OngoingStubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
John McCarthy
sumber
ini sebagian juga membantu saya ... tetapi apa yang terjadi jika daftar yang Anda harapkan tidak kosong?
ttati
alih-alih memiliki daftar kosong, Anda juga dapat melakukannya: Daftar <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati
32

Saya berasumsi Anda ingin dapat memuat someListdengan beberapa nilai yang diketahui; inilah pendekatan yang digunakan Answer<T>bersama-sama dengan metode pembantu templated untuk menjaga semua tipe-aman:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
    DummyClass dummyClass =  Mockito.mock(DummyClass.class);

    Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
    Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);

    ...
}

private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
    final List<N> someList = new ArrayList<N>();

    someList.addAll(Arrays.asList(values));

    Answer<List<N>> answer = new Answer<List<N>>() {
        public List<N> answer(InvocationOnMock invocation) throws Throwable {
            return someList;
        }   
    };
    return answer;
}
rumah pabrik
sumber
17

Saya memukul hal yang sama kemarin. Kedua jawaban dari @nondescript1 dan @millhouse membantu saya menemukan solusi. Saya sudah cukup banyak menggunakan kode yang sama dengan @millhouse, kecuali bahwa saya membuatnya sedikit lebih umum, karena kesalahan saya bukan disebabkan oleh java.util.List, tetapi com.google.common.base.Optional. Metode pembantu kecil saya memungkinkan untuk semua jenis Tdan bukan hanya List<T>:

public static <T> Answer<T> createAnswer(final T value) {
    Answer<T> dummy = new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return value;
        }
    };
    return dummy;
}

Dengan metode bantuan ini Anda dapat menulis:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

Ini mengkompilasi dengan baik dan melakukan hal yang sama seperti thenReturn(...)metode.

Apakah ada yang tahu jika kesalahan yang dihasilkan oleh kompiler Java adalah bug kompiler atau jika kode tersebut benar-benar salah?

Marek Radonsky
sumber
Ini kelihatannya mudah, sederhana, dan sedekat yang saya tahu, benar. Saya tidak yakin mengapa Mockito tidak memberikan sesuatu yang mirip dengan ini ....... kecuali jika tidak?
vacao
14
Di Java 8 dapat disingkat Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)
:,
1
@fikovnik Sungguh penemuan hebat "thenAnswer"!
borjab
5

Saya mengubah komentar fikovnik menjadi jawaban di sini untuk memberikan lebih banyak visibilitas karena saya pikir ini solusi paling elegan menggunakan Java 8+.

The dokumentasi Mockito merekomendasikan menggunakandoReturn() (seperti yang disarankan dalam jawaban diterima) hanya sebagai pilihan terakhir.

Sebagai gantinya, untuk menghindari kesalahan kompiler yang dijelaskan dalam pertanyaan, when()pendekatan Mockito yang direkomendasikan dapat digunakan dengan thenAnswer()dan lambda (alih-alih metode penolong):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
cara lain
sumber
meskipun itu tidak memberikan kesalahan waktu kompilasi, daftar yang dikembalikan kosong bahkan ketika kita melewati daftar dengan entri.
Venkatesh Kolla - user2742897
0

Meskipun metode utilitas yang diusulkan oleh Marek Radonsky berfungsi, ada juga opsi lain yang bahkan tidak memerlukan (lamban tampak aneh) ekspresi fikovnik menyarankan:

Seperti yang ditunjukkan oleh jawaban untuk pertanyaan serupa ini, Anda juga dapat menggunakan yang berikut:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Andreas Siegel
sumber