Cara mencocokkan vararg dengan benar di Mockito

152

Saya sudah mencoba untuk mengejek metode dengan parameter vararg menggunakan Mockito:

interface A {
  B b(int x, int y, C... c);
}

A a = mock(A.class);
B b = mock(B.class);

when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);
assertEquals(b, a.b(1, 2));

Ini tidak berfungsi, namun jika saya melakukan ini sebagai gantinya:

when(a.b(anyInt(), anyInt())).thenReturn(b);
assertEquals(b, a.b(1, 2));

Ini berhasil, meskipun begitu saya telah sepenuhnya menghilangkan argumen varargs ketika mematikan metode.

Ada petunjuk?

kualidafial
sumber
fakta bahwa contoh terakhir berfungsi agak sepele karena cocok dengan kasus ketika nol parameter vararg lewat.
topchef

Jawaban:

235

Mockito 1.8.1 memperkenalkan pencocokan anyargar () :

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);

Lihat juga riwayat untuk ini: https://code.google.com/archive/p/mockito/issues/62

Edit sintaks baru setelah penghentian:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);
topchef
sumber
26
anyVararg()memiliki Object sebagai tipe pengembaliannya. Untuk membuatnya kompatibel dengan semua jenis var arg (mis. String ..., Integer ..., dll.), Lakukan casting secara eksplisit. Misalnya, jika sudah, doSomething(Integer number, String ... args)Anda dapat melakukan kode tiruan / rintisan dengan sesuatu seperti when(mock).doSomething(eq(1), (String) anyVarargs()). Itu harus mengurus kesalahan kompilasi.
Psycho Punch
15
untuk info anyVararg sekarang sudah tidak digunakan lagi: "@deprecated mulai dari 2.1.0 use any ()"
alexbt
5
Matcherssekarang tidak digunakan lagi untuk menghindari benturan nama dengan org.hamcrest.Matcherskelas dan kemungkinan akan dihapus di mockito v3.0. Gunakan ArgumentMatcherssebagai gantinya.
JonyD
31

Fitur yang agak tidak berdokumen: Jika Anda ingin mengembangkan Pencocokan khusus yang cocok dengan argumen vararg, Anda harus menerapkannya org.mockito.internal.matchers.VarargMatcheragar dapat berfungsi dengan benar. Ini adalah antarmuka marker kosong, yang tanpanya Mockito tidak akan membandingkan argumen dengan benar saat menggunakan metode dengan varargs menggunakan Matcher Anda.

Sebagai contoh:

class MyVarargMatcher extends ArgumentMatcher<C[]> implements VarargMatcher {
    @Override public boolean matches(Object varargArgument) {
        return /* does it match? */ true;
    }
}

when(a.b(anyInt(), anyInt(), argThat(new MyVarargMatcher()))).thenReturn(b);
Eli Levine
sumber
7

Membangun jawaban Eli Levine di sini adalah solusi yang lebih umum:

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.VarargMatcher;

import static org.mockito.Matchers.argThat;

public class VarArgMatcher<T> extends ArgumentMatcher<T[]> implements VarargMatcher {

    public static <T> T[] varArgThat(Matcher<T[]> hamcrestMatcher) {
        argThat(new VarArgMatcher(hamcrestMatcher));
        return null;
    }

    private final Matcher<T[]> hamcrestMatcher;

    private VarArgMatcher(Matcher<T[]> hamcrestMatcher) {
        this.hamcrestMatcher = hamcrestMatcher;
    }

    @Override
    public boolean matches(Object o) {
        return hamcrestMatcher.matches(o);
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("has varargs: ").appendDescriptionOf(hamcrestMatcher);
    }

}

Maka Anda bisa menggunakannya dengan pencocokan array hamcrest sebagai berikut:

verify(a).b(VarArgMatcher.varArgThat(
            org.hamcrest.collection.IsArrayContaining.hasItemInArray("Test")));

(Jelas impor statis akan membuat ini lebih mudah dibaca.)

Peter Westmacott
sumber
Bagus. Ini harus dibangun ke IMO Mockito.
bryant
Saya mengajukan masalah terhadap Hamcrest untuk menambahkan sesuatu seperti ini. Lihat github.com/mockito/mockito/issues/356
Mark
Apakah ini untuk Mockito 1? Saya mendapatkan berbagai kesalahan kompilasi ketika mencoba melakukan kompilasi terhadap 2.10.
Frans
@ Trans sepertinya rilis 2 masih dalam versi beta ketika saya menulis jawaban ini, jadi ya itu mungkin ditulis untuk Mockito v1.10.19 atau sekitar itu. ( github.com/mockito/mockito/releases ) Mungkin bisa diperbarui ... :-D
Peter Westmacott
3

Saya telah menggunakan kode dalam jawaban Peter Westmacott namun dengan Mockito 2.2.15 sekarang Anda dapat melakukan hal berikut:

verify(a).method(100L, arg1, arg2, arg3)

dimana arg1, arg2, arg3varargs

Menandai
sumber
1

Membangun jawaban topchef,

Untuk 2.0.31-beta saya harus menggunakan Mockito.anyVararg alih-alih Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
NPike
sumber
3
untuk info anyVararg sekarang sudah tidak digunakan lagi: "@deprecated mulai dari 2.1.0 use any ()"
alexbt
0

Dalam kasus saya, tanda tangan dari metode yang ingin saya tangkap argumennya adalah:

    public byte[] write(byte ... data) throws IOException;

Dalam hal ini Anda harus menggunakan array byte secara eksplisit:

       when(spi.write((byte[])anyVararg())).thenReturn(someValue);

Saya menggunakan versi mockito 1.10.19

Sayur Jalal Hosseini
sumber
0

Anda juga dapat mengulang argumen:

Object[] args = invocation.getArguments(); 
for( int argNo = 0; argNo < args.length; ++argNo) { 
    // ... do something with args[argNo] 
}

misalnya memeriksa tipenya dan melemparkannya secara tepat, menambah daftar atau apa pun.

Richard Whitehead
sumber
0

Mengadaptasi jawaban dari @topchef,

Mockito.when(a.b(Mockito.anyInt(), Mockito.anyInt(), Mockito.any())).thenReturn(b);

Per dokumen java untuk Mockito 2.23.4, Mockito.any () "Cocok dengan apa pun, termasuk null dan varargs."

Craig
sumber