Pencocok mockito adalah metode statis dan panggilan ke metode tersebut, yang menggantikan argumen selama panggilan ke when
dan verify
.
Pencocokan Hamcrest (versi yang diarsipkan) (atau pencocokan gaya Hamcrest) adalah instance objek tujuan umum tanpa kewarganegaraan yang mengimplementasikan Matcher<T>
dan mengekspos metode matches(T)
yang mengembalikan nilai true jika objek cocok dengan kriteria Matcher. Mereka dimaksudkan agar bebas dari efek samping, dan umumnya digunakan dalam pernyataan seperti di bawah ini.
/* Mockito */ verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));
Pencocokan mockito ada, terpisah dari pencocokan gaya Hamcrest, sehingga deskripsi ekspresi yang cocok langsung masuk ke dalam pemanggilan metode : Pencocokan mockito kembali di T
mana metode pencocok Hamcrest mengembalikan objek Matcher (jenis Matcher<T>
).
Mockito matchers dipanggil melalui metode statis seperti eq
, any
, gt
, dan startsWith
di org.mockito.Matchers
dan org.mockito.AdditionalMatchers
. Ada juga adaptor, yang telah berubah di seluruh versi Mockito:
- Untuk Mockito 1.x,
Matchers
menampilkan beberapa panggilan (seperti intThat
atau argThat
) adalah pencocok Mockito yang secara langsung menerima pencocok Hamcrest sebagai parameter. ArgumentMatcher<T>
extended org.hamcrest.Matcher<T>
, yang digunakan dalam representasi internal Hamcrest dan merupakan kelas dasar matcher Hamcrest alih-alih segala jenis Mockito matcher.
- Untuk Mockito 2.0+, Mockito tidak lagi memiliki ketergantungan langsung pada Hamcrest.
Matchers
panggilan yang diutarakan sebagai intThat
atau argThat
membungkus ArgumentMatcher<T>
objek yang tidak lagi diimplementasikan org.hamcrest.Matcher<T>
tetapi digunakan dengan cara yang serupa. Adaptor Hamcrest seperti argThat
dan intThat
masih tersedia, tetapi MockitoHamcrest
sebagai gantinya telah dipindahkan .
Terlepas dari apakah matcher tersebut adalah Hamcrest atau hanya bergaya Hamcrest, mereka dapat diadaptasi seperti ini:
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));
Dalam pernyataan di atas: foo.setPowerLevel
adalah metode yang menerima file int
. is(greaterThan(9000))
mengembalikan a Matcher<Integer>
, yang tidak akan berfungsi sebagai setPowerLevel
argumen. intThat
Pencocok Mockito membungkus Pencocokan gaya-Hamcrest dan mengembalikan int
sehingga dapat muncul sebagai argumen; Pencocok mockito ingin menggabungkan gt(9000)
seluruh ekspresi itu menjadi satu panggilan, seperti pada baris pertama kode contoh.
Apa yang dilakukan / dikembalikan oleh pencocokkan
when(foo.quux(3, 5)).thenReturn(true);
Saat tidak menggunakan pencocok argumen, Mockito mencatat nilai argumen Anda dan membandingkannya dengan equals
metodenya.
when(foo.quux(eq(3), eq(5))).thenReturn(true); // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different
Saat Anda memanggil matcher like any
atau gt
(lebih besar dari), Mockito menyimpan objek matcher yang menyebabkan Mockito melewati pemeriksaan kesetaraan itu dan menerapkan kecocokan pilihan Anda. Dalam kasus argumentCaptor.capture()
itu menyimpan matcher yang menyimpan argumennya sebagai gantinya untuk pemeriksaan nanti.
Pencocokan mengembalikan nilai dummy seperti nol, koleksi kosong, atau null
. Mockito mencoba mengembalikan nilai dummy yang sesuai dan aman, seperti 0 untuk anyInt()
atau any(Integer.class)
atau kosong List<String>
untuk anyListOf(String.class)
. Karena penghapusan jenis, Mockito kekurangan informasi jenis untuk mengembalikan nilai apa pun kecuali null
untuk any()
atau argThat(...)
, yang dapat menyebabkan NullPointerException jika mencoba "membuka kotak otomatis" null
nilai primitif.
Pencocokan menyukai eq
dan gt
mengambil nilai parameter; idealnya, nilai-nilai ini harus dihitung sebelum stubbing / verifikasi dimulai. Memanggil tiruan di tengah-tengah mengejek panggilan lain dapat mengganggu penyumbatan.
Metode matcher tidak bisa digunakan sebagai nilai kembali; tidak ada cara untuk mengucapkan thenReturn(anyInt())
atau thenReturn(any(Foo.class))
dalam Mockito, misalnya. Mockito perlu tahu persis instance mana yang akan dikembalikan dalam panggilan stubbing, dan tidak akan memilih nilai pengembalian yang sewenang-wenang untuk Anda.
Detail implementasi
Pencocokan disimpan (sebagai pencocokkan objek gaya Hamcrest) dalam tumpukan yang terdapat dalam kelas yang disebut ArgumentMatcherStorage . MockitoCore dan Matcher masing-masing memiliki instance ThreadSafeMockingProgress , yang secara statis berisi instance MockingProgress yang menampung ThreadLocal. Ini ini MockingProgressImpl yang memegang beton ArgumentMatcherStorageImpl . Akibatnya, status tiruan dan pencocokan bersifat statis tetapi memiliki cakupan thread yang konsisten antara kelas Mockito dan Matchers.
Kebanyakan panggilan matcher hanya menambah tumpukan ini, dengan pengecualian untuk matchers seperti and
, or
, dannot
. Ini sangat sesuai dengan (dan bergantung pada) urutan evaluasi Java , yang mengevaluasi argumen kiri-ke-kanan sebelum menggunakan metode:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
Ini akan:
- Tambahkan
anyInt()
ke tumpukan.
- Tambahkan
gt(10)
ke tumpukan.
- Tambahkan
lt(20)
ke tumpukan.
- Hapus
gt(10)
dan lt(20)
dan tambahkan and(gt(10), lt(20))
.
- Panggilan
foo.quux(0, 0)
, yang (kecuali jika dihentikan) mengembalikan nilai default false
. Secara internal Mockito menandai quux(int, int)
sebagai panggilan terbaru.
- Panggil
when(false)
, yang membuang argumennya dan bersiap untuk metode stub yang quux(int, int)
diidentifikasi di 5. Dua status valid hanya dengan panjang tumpukan 0 (persamaan) atau 2 (pencocokan), dan ada dua pencocokan di tumpukan (langkah 1 dan 4), jadi Mockito menghentikan metode dengan any()
matcher untuk argumen pertama dan and(gt(10), lt(20))
untuk argumen kedua dan membersihkan tumpukan.
Ini menunjukkan beberapa aturan:
Mockito tidak bisa membedakan antara quux(anyInt(), 0)
dan quux(0, anyInt())
. Mereka berdua terlihat seperti panggilan ke quux(0, 0)
dengan satu pencocok int di tumpukan. Akibatnya, jika Anda menggunakan satu matcher, Anda harus mencocokkan semua argumen.
Urutan panggilan tidak hanya penting, itu yang membuat semua ini berfungsi . Mengekstrak matcher ke variabel umumnya tidak berfungsi, karena biasanya mengubah urutan panggilan. Namun, mengekstrak matcher ke metode berfungsi dengan baik.
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.
Tumpukan tersebut cukup sering berubah sehingga Mockito tidak dapat mengawasi dengan sangat hati-hati. Itu hanya dapat memeriksa tumpukan saat Anda berinteraksi dengan Mockito atau tiruan, dan harus menerima pencocokan tanpa mengetahui apakah mereka digunakan segera atau ditinggalkan secara tidak sengaja. Secara teori, tumpukan harus selalu kosong di luar panggilan ke when
atau verify
, tetapi Mockito tidak dapat memeriksanya secara otomatis. Anda dapat memeriksanya secara manual dengan Mockito.validateMockitoUsage()
.
Dalam panggilan ke when
, Mockito sebenarnya memanggil metode yang dimaksud, yang akan memunculkan pengecualian jika Anda telah menghentikan metode untuk membuat pengecualian (atau memerlukan nilai bukan nol atau bukan nol).
doReturn
dan doAnswer
(dll) tidak menggunakan metode sebenarnya dan sering kali merupakan alternatif yang berguna.
Jika Anda telah disebut metode mock di tengah-tengah Stubbing (misalnya untuk menghitung jawaban untuk eq
matcher), Mockito akan memeriksa panjang tumpukan melawan bahwa panggilan sebagai gantinya, dan kemungkinan gagal.
Jika Anda mencoba melakukan sesuatu yang buruk, seperti menghentikan / memverifikasi metode terakhir , Mockito akan memanggil metode yang sebenarnya dan juga meninggalkan pencocok ekstra di tumpukan . The final
pemanggilan metode mungkin tidak membuang pengecualian, tetapi Anda mungkin mendapatkan InvalidUseOfMatchersException dari matchers liar ketika Anda berinteraksi berikutnya dengan pura-pura.
Masalah umum
InvalidUseOfMatchersException :
Periksa bahwa setiap argumen memiliki tepat satu pemanggil pencocokan, jika Anda menggunakan pencocokan sama sekali, dan bahwa Anda belum menggunakan pencocokan di luar panggilan when
atau verify
. Pencocokan tidak boleh digunakan sebagai nilai kembali atau bidang / variabel yang dipotong.
Pastikan Anda tidak memanggil tiruan sebagai bagian dari memberikan argumen matcher.
Periksa apakah Anda tidak mencoba untuk menghentikan / memverifikasi metode terakhir dengan pencocok. Ini cara yang bagus untuk meninggalkan matcher di tumpukan, dan kecuali metode terakhir Anda mengeluarkan pengecualian, ini mungkin satu-satunya saat Anda menyadari bahwa metode yang Anda ejekan sudah final.
NullPointerException dengan argumen primitif: (Integer) any()
mengembalikan null sedangkan any(Integer.class)
mengembalikan 0; ini dapat menyebabkan NullPointerException
jika Anda mengharapkan, int
bukan Integer. Bagaimanapun, prefer anyInt()
, yang akan mengembalikan nol dan juga melewatkan langkah auto-boxing.
NullPointerException atau pengecualian lainnya: Panggilan ke when(foo.bar(any())).thenReturn(baz)
akan benar-benar memanggil foo.bar(null)
, yang mungkin telah Anda stub untuk membuat pengecualian saat menerima argumen null. Beralih untuk doReturn(baz).when(foo).bar(any())
melewati perilaku yang dihentikan .
Pemecahan masalah umum
Gunakan MockitoJUnitRunner , atau panggil metode atau validateMockitoUsage
Anda secara eksplisit (yang akan dilakukan pelari untuk Anda secara otomatis). Ini akan membantu menentukan apakah Anda telah menyalahgunakan matcher.tearDown
@After
Untuk tujuan debugging, tambahkan panggilan ke validateMockitoUsage
dalam kode Anda secara langsung. Ini akan dibuang jika Anda memiliki sesuatu di tumpukan, yang merupakan peringatan yang baik untuk gejala yang buruk.
Hanya sedikit tambahan untuk jawaban luar biasa Jeff Bowman, karena saya menemukan pertanyaan ini saat mencari solusi untuk salah satu masalah saya sendiri:
Jika panggilan ke suatu metode cocok dengan lebih dari satu
when
panggilan terlatih tiruan , urutanwhen
panggilan itu penting, dan harus dari yang paling luas ke paling spesifik. Mulai dari salah satu contoh Jeff:adalah urutan yang memastikan hasil yang (mungkin) diinginkan:
Jika Anda membalik ketika panggilan maka hasilnya akan selalu
true
.sumber