Mockito cocok dengan argumen kelas apa pun

153

Apakah ada cara untuk mencocokkan argumen kelas dari rutin sampel di bawah ini?

class A {
     public B method(Class<? extends A> a) {}
}

Bagaimana saya bisa selalu mengembalikan kelas new B()apa pun yang dilewatkan method? Upaya berikut ini hanya berfungsi untuk kasus tertentu Ayang cocok.

A a = new A();
B b = new B();
when(a.method(eq(A.class))).thenReturn(b);

EDIT : Salah satu solusinya adalah

(Class<?>) any(Class.class)
Johan Sjöberg
sumber
6
Class<?>luar biasa!
António Almeida
Solusi Anda (Kelas <?>) Apa saja (Kelas.class) harus menjadi jawabannya di sini. Saya lebih suka menggunakannya daripada kelas ClassOrSubclassMatcher yang terlihat di bawah ini.
superbAfterSemperPhi
@superbAfterSemperPhi dan johan-sjöberg saya memposting cara lain untuk melakukan itu, tanpa pemain. Saya percaya itu bisa menjadi cara yang lebih baik. Bagaimana menurut anda?
Anmaia

Jawaban:

188

Dua cara lain untuk melakukannya (lihat komentar saya pada jawaban sebelumnya oleh @Tomasz Nurkiewicz):

Yang pertama bergantung pada fakta bahwa kompiler tidak akan membiarkan Anda memasukkan sesuatu dengan tipe yang salah:

when(a.method(any(Class.class))).thenReturn(b);

Anda kehilangan pengetikan yang tepat (the Class<? extends A>) tetapi mungkin berhasil sesuai keinginan Anda.

Yang kedua jauh lebih terlibat tetapi bisa dibilang solusi yang lebih baik jika Anda benar - benar ingin memastikan bahwa argumen untuk method()adalah Aatau subkelas dari A:

when(a.method(Matchers.argThat(new ClassOrSubclassMatcher<A>(A.class)))).thenReturn(b);

Dimana ClassOrSubclassMatchermerupakan org.hamcrest.BaseMatcherdidefinisikan sebagai:

public class ClassOrSubclassMatcher<T> extends BaseMatcher<Class<T>> {

    private final Class<T> targetClass;

    public ClassOrSubclassMatcher(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @SuppressWarnings("unchecked")
    public boolean matches(Object obj) {
        if (obj != null) {
            if (obj instanceof Class) {
                return targetClass.isAssignableFrom((Class<T>) obj);
            }
        }
        return false;
    }

    public void describeTo(Description desc) {
        desc.appendText("Matches a class or subclass");
    }       
}

Fiuh! Saya akan menggunakan opsi pertama sampai Anda benar - benar perlu mendapatkan kontrol yang lebih baik atas apa yang method()sebenarnya dikembalikan :-)

rumah pabrik
sumber
yang if (obj instanceof Class)messes segalanya bagi saya, jadi saya dihapus.
Daniel Smith
Pada baris deklarasi kelas, saya harus berubah extends BaseMatcher<Class<T>>menjadi adil extends BaseMatcher<T>. Hanya FYI, jika orang lain mendapat kesalahan kompilasi, coba itu.
Jan
Saya juga harus mengubah matchesfungsi sebagai berikut:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Jan
any (Class.class) mengembalikan null - bagaimana saya bisa menghindari untuk mengembalikan null
Arvind Kumar
akan lebih bagus jika benar-benar menambahkan kelas yang perlu saya impor karena sekarang banyak "ada" dari mockito
jpganz18
53

Ada cara lain untuk melakukannya tanpa pemain:

when(a.method(Matchers.<Class<A>>any())).thenReturn(b);

Solusi ini memaksa metode any()untuk mengembalikan Class<A>tipe dan bukan nilai standarnya ( Object).

anmaia
sumber
5
Matcherstidak lagi digunakan dalam versi Mockito yang lebih baru dan kemungkinan akan dihapus di versi 3.0. Gunakan ArgumentMatcherssebagai gantinya:when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
41

Jika Anda tidak tahu Paket mana yang perlu Anda impor:

import static org.mockito.ArgumentMatchers.any;
any(SomeClass.class)

ATAU

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
Joao Luiz Cadore
sumber
13
Ini menyelamatkan hidup saya, saya tidak sengaja mengimpor "any" dari perpustakaan hamcrest.
Gábor Nagy
3
Sekarang telah berubah menjadiorg.mockito.ArgumentMatchers.any
BOWS
27

Bagaimana tentang:

when(a.method(isA(A.class))).thenReturn(b);

atau:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
sumber
4
Ini akan dikompilasi dan berfungsi jika metode signature adalah method(A a)- tapi itu (efektif) method(Class<A> a)- jadi Anda harus menggunakan: when(a.method(isA(Class.class))).thenReturn(b);atauwhen(a.method((Class<A>) notNull())).thenReturn(b);
millhouse
bagian kedua bagi saya berfungsi sebagai pesona. berkelahi dengan (SomeClass.class) mengarah ke jalan buntu. Tapi (SomeClass.class) notNull () menyelamatkan hari saya
Vadim
Jika Anda memiliki dua metode dengan nama yang sama tetapi argumen yang berbeda, Anda dapat menyamarkan metode yang akan diejek menggunakan versi kedua di sini. Versi pertama tidak memotongnya untuk saya (di Java 8 yaitu).
Patru
Terima kasih, isA (A.class) berfungsi baik untuk saya dan mvcConversionService memilih kelas yang tepat. Ini tidak bekerja dengan (A.class) dan eq (A.class).
d3rbastl3r
9

solusi dari millhouse tidak berfungsi lagi dengan versi terbaru dari mockito

Solusi ini berfungsi dengan java 8 dan mockito 2.2.9

di mana ArgumentMatcherada instanceoforg.mockito.ArgumentMatcher

public class ClassOrSubclassMatcher<T> implements ArgumentMatcher<Class<T>> {

   private final Class<T> targetClass;

    public ClassOrSubclassMatcher(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public boolean matches(Class<T> obj) {
        if (obj != null) {
            if (obj instanceof Class) {
                return targetClass.isAssignableFrom( obj);
            }
        }
        return false;
    }
}

Dan penggunaannya

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cedric
sumber
kondisi instanceof tidak diperlukan lagi, dan saya menulis metode kenyamanan:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Daniel Alder