Mockito dan cara memasaknya

Tentang artikel


Berikut ini panduan lain untuk Mockito. Di dalamnya, di satu sisi, saya mencoba mendeskripsikan fungsionalitas perpustakaan ini sehingga pembaca yang tidak terbiasa dengan itu segera mendapat kesempatan untuk sepenuhnya menggunakannya, dan bukan hanya gagasan umum tentang itu. Di sisi lain, saya ingin membuatnya cukup ringkas dan terstruktur sehingga saya dapat dengan cepat membacanya secara keseluruhan dan dengan cepat menemukan sesuatu yang pernah dibaca, tetapi dilupakan. Secara umum, artikel ini, yang akan berguna bagi saya sendiri, ketika saya baru saja menemukan perpustakaan ini dan tidak benar-benar mengerti cara kerjanya.


Saya kira itu bisa berguna bagi saya sekarang - kadang-kadang saya lupa beberapa hal ini, dan lebih mudah untuk mengingat materi tidak sesuai dengan dokumentasi resmi atau artikel orang lain, tetapi menurut saya sendiri, katakanlah, sinopsis. Pada saat yang sama, saya mencoba untuk membuat teks sedemikian rupa sehingga nyaman terutama untuk menjelajahi Mockito dari awal, dan di beberapa tempat saya menganalisis secara terperinci hal-hal yang tampaknya jelas - tidak semuanya jelas bagi saya sejak awal.


Konten:


  1. Mockito: apa itu dan mengapa itu perlu
  2. Lingkungan, versi dan hewan percobaan
  3. mengejek dan memata-matai
  4. Manajemen perilaku
    1. Pengaturan kondisi panggilan
    2. Mengatur Hasil Panggilan
  5. Panggilan Metode Pelacakan
  6. Mock objek sebagai nilai bidang dan anotasi Mockito
  7. Kembalikan perilaku ke sesi default dan Mockito
  8. Apa lagi

Mockito: apa itu dan mengapa itu perlu


Singkatnya, Mockito adalah kerangka kerja rintisan.


Seperti yang Anda ketahui, saat menguji kode (terutama pengujian unit, tetapi tidak hanya), elemen yang diuji sering perlu memberikan contoh kelas yang harus digunakan saat bekerja. Namun, seringkali mereka tidak harus berfungsi penuh - sebaliknya, mereka dituntut untuk berperilaku dengan cara yang didefinisikan secara ketat, sehingga perilaku mereka sederhana dan dapat diprediksi sepenuhnya. Mereka disebut bertopik. Untuk mendapatkannya, Anda bisa membuat implementasi uji alternatif dari antarmuka, mewarisi kelas yang diperlukan dengan redefinisi fungsi, dan sebagainya, tetapi semua ini cukup merepotkan, berlebihan dan penuh dengan kesalahan. Solusi yang lebih nyaman dalam semua pengertian adalah kerangka kerja khusus untuk membuat bertopik. Salah satunya (dan mungkin yang paling terkenal di Jawa) adalah Mockito.


Mockito memungkinkan Anda untuk membuat dengan satu baris kode yang disebut mock (sesuatu seperti dasar untuk rintisan yang diinginkan) dari kelas mana pun. Untuk tiruan seperti itu, segera setelah pembuatan, perilaku default tertentu adalah karakteristik (semua metode mengembalikan nilai yang diketahui sebelumnya - biasanya ini null atau 0 ). Anda dapat mendefinisikan kembali perilaku ini seperti yang Anda inginkan, mengendalikannya dengan tingkat detail yang tepat, dan sebagainya. Akibatnya, tiruan menjadi rintisan dengan properti yang diperlukan. Di bawah ini saya akan membahas secara rinci bagaimana melakukan ini.


Saya perhatikan bahwa mock juga dapat dibuat untuk kelas-kelas itu, contoh baru yang sebenarnya tidak bisa Anda buat, khususnya, kelas-kelas dengan konstruktor khusus pribadi seperti kelas-kelas utilitas dan tunggal, dan dengan konfigurasi kerangka kerja minimal, dan enum.


Lingkungan, versi dan hewan percobaan


Saat menulis artikel ini, saya menggunakan:


  • Mockito: 'org.mockito: mockito-core: 2.24.0' (versi stabil terbaru saat penulisan)
  • TestNG: 'org.testng: testng: 6.14.3' sebagai kerangka uji
  • AssertJ: 'org.assertj: assertj-core: 3.11.1' sebagai alat validasi
  • Lombok: 'org.projectlombok: lombok: 1.18.6' (hanya untuk kenyamanan)
  • Java 8

Untuk eksperimen tidak manusiawi saya, saya menulis antarmuka layanan yang menyediakan akses ke data tertentu.


 public interface DataService { void saveData(List<String> dataToSave); String getDataById(String id); String getDataById(String id, Supplier<String> calculateIfAbsent); List<String> getData(); List<String> getDataListByIds(List<String> idList); List<String> getDataByRequest(DataSearchRequest request); } 

Dan ini (biarkan demi pesanan) kode dari kelas permintaan diteruskan ke yang terakhir dari metode antarmuka.


 @AllArgsConstructor @Getter class DataSearchRequest { String id; Date updatedBefore; int length; } 

Unit data diidentifikasi oleh ID dan memiliki beberapa karakteristik lebih, tetapi langsung dalam bentuk di mana mereka dikembalikan oleh layanan, mereka adalah string, dan bukan beberapa objek yang lebih kompleks. Saya tidak ketinggalan sesuatu yang penting, dan contohnya lebih sederhana dan lebih jelas.


Saya akan segera mencatat: dalam contoh di bawah ini, saya langsung memanggil metode yang ditimpa objek tiruan saya untuk kejelasan, tetapi dengan pengujian nyata idenya tidak sama sekali! Dalam tes ini, saya akan secara konsisten melakukan hal berikut:


  • mengkonfigurasi tiruan layanan saya sesuai kebutuhan;
  • melewatinya (kemungkinan besar, melalui konstruktor) ke instance dari kelas lain yang menggunakannya (misalkan berisi semacam logika bisnis menggunakan data yang disediakan oleh DataService ), yang sebenarnya akan saya uji;
  • mengaktifkan fungsionalitas kelas yang diuji dan mengontrol hasilnya;
  • jika perlu, saya akan mengontrol jumlah dan urutan panggilan ke metode mock saya, yang seharusnya dipanggil oleh kelas yang diuji sebagai hasil dari tindakan sebelumnya.

mengejek dan memata-matai


Kelas pusat Mockito, di mana ia seharusnya mengakses sebagian besar fungsi, adalah, pada kenyataannya, kelas yang disebut Mockito (ada juga kelas BDDMockito yang menyediakan tentang kemungkinan yang sama dalam bentuk yang lebih cocok untuk BDD , tapi di sini saya tidak akan membahasnya) . Akses ke fungsi diimplementasikan melalui metode statisnya.


Untuk membuat tiruan dari kelas DataService , saya hanya perlu melakukan hal berikut:


 DataService dataServiceMock = Mockito.mock(DataService.class); 

Selesai - Saya mendapat instance dari kelas yang saya butuhkan. Ini akan diterima oleh metode atau konstruktor apa pun yang memerlukan parameter jenis ini (misalnya, konstruktor dari kelas yang ingin saya uji). Bahkan jika pemeriksaan kecanduan menunggu nanti, ia akan meneruskannya: tidak hanya instanceof DataService akan mengembalikan true , tetapi juga dataServiceMock.getClass() - yaitu DataService.class . Dalam beberapa cara formal, untuk secara terprogram membedakan objek tiruan dari yang biasa ternyata menjadi tugas yang agak sulit, yang logis: setelah semua, yang pertama dimaksudkan hanya untuk bisa dibedakan dari yang kedua. Namun, Mockito memiliki alat untuk ini - metode Mockito.mockingDetails . Dengan melewatkannya objek sewenang-wenang, saya mendapatkan objek dari kelas MockingDetails . Ini berisi informasi tentang apa yang diwakili objek ini dari sudut pandang Mockito: apakah itu tiruan, mata-mata (lihat di bawah), bagaimana benda itu digunakan, bagaimana benda itu dibuat, dan sebagainya.


Dari catatan khusus adalah situasi ketika saya mencoba membuat tiruan untuk kelas akhir atau contoh tiruan dari enum atau untuk menimpa perilaku metode akhir. Dalam hal ini, dengan perilaku default Mockito, kode di atas menolak untuk bekerja, dengan mengutip keadaan ini. Namun, ini dapat diubah - cukup buat dalam proyek (dengan perangkat standar dari pohon direktori proyek) test/resources/mockito-extensions/org.mockito.plugins.MockMaker file test/resources/mockito-extensions/org.mockito.plugins.MockMaker dan masukkan baris di dalamnya:


 mock-maker-inline 

Setelah itu, Anda dapat meniru kelas akhir dan enum dengan cara biasa, serta mengganti metode final.


Mock yang saya dapatkan dalam aksi adalah sebagai fitur mungkin: tidak ada satu metode akan memiliki efek pada apa pun, dan nilai yang dikembalikan akan null untuk jenis objek dan 0 untuk yang primitif. Harap dicatat: jika metode mengembalikan koleksi, tiruan standar tidak akan mengembalikan null , tetapi mengosongkan koleksi contoh. Misalnya, untuk List ini akan berubah menjadi LinkedList kosong LinkedList terlepas dari apa metode yang seharusnya dikembalikan. Tetapi sebagai nilai array, primitif atau objek, saya mendapatkan null . Perilaku default (dan bukan hanya itu) dapat diubah menggunakan fungsi dari kelas MockSettings , tetapi ini jarang diperlukan.


Dengan satu atau lain cara, dalam kebanyakan kasus saya tidak akan memerlukan perilaku default, dan pada bagian selanjutnya saya akan menganalisis secara rinci bagaimana mengatur apa yang diperlukan sebagai gantinya.


Namun, bagaimana jika saya ingin menggunakan objek kelas nyata dengan fungsi yang tersedia sebagai rintisan, mendefinisikan ulang operasi hanya sebagian dari metodenya? Jika kita berbicara tentang pengujian unit, kebutuhan seperti itu biasanya (tetapi tidak selalu) menunjukkan bahwa proyek tersebut tidak sepenuhnya sesuai dengan desain, dan pada prinsipnya, ini tidak direkomendasikan. Namun, ada situasi ketika ini karena alasan tertentu tidak dapat dihindari. Untuk kasus ini, Mockito memiliki mata-mata yang disebut, "mata-mata." Tidak seperti mock, mereka dapat dibuat berdasarkan kelas dan objek yang sudah jadi:


 DataService dataServiceSpy = Mockito.spy(DataService.class); // or DataService dataService = new DataService(); dataServiceSpy = Mockito.spy(dataService); 

Saat membuat mata-mata berdasarkan kelas, jika tipenya adalah antarmuka, objek tiruan biasa akan dibuat, dan jika tipenya adalah kelas, maka Mockito akan mencoba membuat instance menggunakan konstruktor default (tanpa parameter). Dan hanya jika tidak ada konstruktor seperti itu, kesalahan akan terjadi dan tes tidak akan berfungsi.


Perilaku default objek mata-mata identik dengan perilaku instance kelas reguler, namun mereka memberi saya kemungkinan yang sama dengan objek tiruan: mereka memungkinkan saya untuk mendefinisikan kembali perilaku mereka dan memantau penggunaannya (lihat bagian berikut). Poin penting: mata-mata bukanlah pembungkus contoh yang dibuatnya! Oleh karena itu, memanggil metode mata-mata tidak akan memengaruhi keadaan instance asli.


Manajemen perilaku


Jadi, tentang cara mendapatkan tiruan atau mata-mata untuk melakukan apa yang saya butuhkan. Lebih jauh, saya akan selalu menulis "mock" di mana-mana - ini berarti "mock atau spy", kecuali dinyatakan lain.


Secara umum, mengendalikan perilaku objek tiruan diturunkan ke satu konsep yang jelas: ketika tiruan dipengaruhi sedemikian rupa (yaitu, metode ini dan itu disebut dengan argumen ini dan itu), ia harus merespons dengan cara ini dan itu. Konsep ini memiliki dua implementasi dalam kelas Mockito - yang utama, direkomendasikan oleh pengembang untuk digunakan sedapat mungkin, dan yang alternatif, digunakan di mana yang utama tidak cocok.


Implementasi utama didasarkan pada metode Mockito.when . Metode ini mengambil sebagai "parameter" panggilan ke metode redefined objek-mock (cara ini tindakan yang terdeteksi diperbaiki) dan mengembalikan objek tipe OngoingStubbing , yang memungkinkan memanggil salah satu metode keluarga Mockito.then... (ini adalah bagaimana reaksi terhadap efek ini diatur). Secara keseluruhan, dalam kasus paling sederhana, terlihat seperti ini:


 List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data); 

Setelah operasi ini, dengan memanggil metode getAllData() pada objek getAllData() , saya mendapatkan objek yang ditentukan di baris pertama daftar.


Di sini, intuisi "berorientasi objek" yang sudah dikenal mungkin memberikan semacam malfungsi, jadi ini layak untuk dibahas lebih rinci. Dari sudut pandang sintaksis Java, nilai yang diteruskan ke metode when sebagai parameter, tentu saja, nilai yang dikembalikan oleh metode yang diganti. Untuk mock, ini adalah nilai kosong, untuk mata-mata, ini adalah nilai yang dikembalikan oleh metode objek nyata. Tetapi berkat sihir yang bertindak "di bawah tenda" Mockito, metode when akan bekerja secara normal (dan tidak akan crash ketika diluncurkan dengan kesalahan) hanya jika panggilan metode objek mock berada di dalam tanda kurung setelah when .


Ideologi serupa sering kali berfungsi ketika mendefinisikan perilaku tiruan dalam Mockito: dengan memanggil metode (objek tiruan atau kelas Mockito ), saya mencoba untuk tidak mendapatkan nilai yang dikembalikan olehnya, tetapi entah bagaimana memengaruhi panggilan yang mungkin dari metode objek tiruan yang bekerja dengan saya: tentukan batas-batasnya, tentukan hasilnya, buat pengamatan tantangannya dan seterusnya. Kedengarannya agak berkabut, saya akui, dan pada tabrakan pertama terlihat aneh, tetapi, setelah mengetahuinya, Anda segera mulai merasakan pendekatan ini sebagai sesuatu yang sepenuhnya alami dalam konteks bekerja dengan bertopik.


Implementasi alternatif untuk menghubungkan kondisi dan hasil panggilan adalah metode dari keluarga Mockito.do... . Metode ini memungkinkan Anda untuk mengatur perilaku yang dimulai dengan hasil panggilan dan mengembalikan objek dari kelas Stubber , yang dengannya Anda sudah dapat mengatur kondisinya. Pengikatan yang sama seperti di atas dilakukan dengan cara ini terlihat seperti ini:


 List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.doReturn(data).when(dataService).getData() 

Apa bedanya, mengapa mengikat melalui Mockito.when dianggap lebih baik dan ketika Anda masih harus menggunakan metode Mockito.do... ? Harap dicatat: dalam implementasi pertama, ketika mengatur perilaku metode (dalam hal ini, getAllData() ), panggilan ke versi yang belum didefinisikan ulang dilakukan terlebih dahulu, dan hanya kemudian, di perut Mockito, terjadi override. Dalam yang kedua, panggilan seperti itu tidak terjadi - metode Stubber.when diteruskan langsung ke metode Stubber.when , dan objek dengan tipe yang sama dikembalikan oleh metode ini tetapi dari sifat yang berbeda disebut dengan metode yang dapat ditimpa. Perbedaan ini menentukan segalanya. Mengikat melalui Mockito.do... sekali tidak mengontrol pada tahap kompilasi metode mana yang akan didefinisikan ulang yang akan saya panggil dan apakah itu kompatibel dengan tipe dengan nilai pengembalian yang diberikan. Karena itu, biasanya Mockito.when lebih disukai - tidak ada kesalahan dengan ini. Tetapi mungkin ada kasus ketika saya ingin menghindari memanggil metode yang diganti - untuk tiruan yang baru dibuat, panggilan seperti itu cukup dapat diterima, tetapi jika saya telah mendefinisikan ulang metode ini atau berurusan dengan mata-mata, itu mungkin tidak diinginkan, dan melemparkan pengecualian tidak akan memungkinkan redefinisi yang diperlukan sama sekali . Dan di sini mengikat melalui Mockito.do... datang untuk Mockito.do...


Situasi lain di mana Anda tidak dapat melakukan tanpa metode Mockito.do... menimpa metode yang mengembalikan void : parameter Mockito.when tertunda ketika tidak dapat bekerja dengan metode seperti itu. Mockito.doReturn , tentu saja, keluar dari pekerjaan, tetapi ada Mockito.doThrow , Mockito.doAnswer . Mockito.doAnswer dan Mockito.doAnswer jarang cukup Mockito.doNothing . Tidak ada.


Selanjutnya, saya akan membahas sedikit lebih rinci cara mengatur kondisi dan hasil panggilan. Saya hanya akan mempertimbangkan untuk mengikat melalui Mockito.when - cara alternatif hampir sepenuhnya sama dalam penanganannya.


Pengaturan kondisi panggilan


Contoh di atas menyangkut metode tanpa parameter, dan kondisi panggilan yang terkait adalah mungkin satu hal - fakta panggilan. Segera setelah parameter muncul, situasinya menjadi lebih rumit. Minimal, untuk memanggil metode yang perilakunya saya setting, saya perlu menyampaikan sesuatu kepadanya. Tetapi satu hal lagi yang lebih penting: mungkin ternyata saya tidak selalu ingin mendapatkan reaksi yang diberikan, tetapi hanya ketika saya menyebutnya dengan parameter yang memenuhi persyaratan tertentu. DataService memiliki metode ini:


 String getDataItemById(String id) { // some code... } 

Jika saya perlu mengatur respons terhadap panggilan apa pun ke metode ini, apa pun argumennya, saya harus menggunakan metode Mockito.any :


 Mockito.when(dataService.getDataItemById(any())) .thenReturn("dataItem"); 

Jika saya perlu mock untuk bereaksi hanya pada nilai argumen tertentu, Anda dapat menggunakan nilai ini secara langsung atau metode Mockito.eq (jika itu adalah kesetaraan) atau Mockito.same (jika diperlukan perbandingan tautan):


 Mockito.when(dataService.getDataItemById("idValue")) .thenReturn("dataItem"); // or Mockito.when(dataService.getDataItemById(Mockito.eq("idValue"))) .thenReturn("dataItem"); 

Dan jika saya ingin argumen memenuhi beberapa persyaratan, ada sejumlah metode statis khusus yang mudah digunakan dari kelas Mockito sama (misalnya, string dapat diperiksa untuk konten di awal atau di akhir urutan karakter tertentu, pencocokan pola, dll.). Ada juga metode umum Mockito.argThat (dan analognya untuk tipe primitif) yang menerima implementasi antarmuka fungsional ArgumentMatcher:


 Mockito.when(dataService.getDataById( Mockito.argThat(arg -> arg == null || arg.length() > 5))) .thenReturn("dataItem"); 

Kelas ArgumentMatchers dan AdditionalMatchers memungkinkan Anda untuk bekerja dengan beberapa implementasi yang berguna dari antarmuka ini. Misalnya, AdditionalMatchers.or dan AdditionalMatchers.and memungkinkan Anda untuk menggabungkan korek api lain (catatan: metode statis kelas ini tidak mengembalikan instance korek api, tetapi hanya mengaksesnya!)


Untuk metode yang sama, Anda bisa mengatur perilaku beberapa kali dengan persyaratan berbeda untuk argumen, dan semua model perilaku yang didefinisikan dengan cara ini akan bertindak secara bersamaan. Tentu saja, dalam beberapa kasus, mereka mungkin berpotongan - katakanlah, saya akan meminta untuk mengembalikan satu hasil ketika nilai int parameter kurang dari 5 dan yang lainnya ketika nilai genap diterima. Dalam situasi ini, perilaku yang ditentukan kemudian diutamakan. Karena itu, ketika mendefinisikan pola perilaku yang kompleks, Anda harus mulai dengan persyaratan terlemah (dalam batas - any() ) dan baru kemudian beralih ke yang lebih spesifik.


Ketika bekerja dengan metode dengan lebih dari satu argumen, persyaratan yang ditentukan digabungkan sesuai dengan logika AND, yaitu, untuk mendapatkan hasil yang diberikan, SETIAP argumen harus memenuhi persyaratan yang dinyatakan. Saya tidak menemukan cara untuk mengatur cara sewenang-wenang untuk menggabungkan mereka, meskipun mungkin ada.


Selain itu, ketika menentukan perilaku metode seperti itu, seseorang tidak dapat menggabungkan metode statis Mockito dan transfer nilai secara langsung. Gunakan Mockito.eq atau Mockito.same .


Mengatur Hasil Panggilan


Setelah metode objek tiruan dipanggil, objek harus merespons panggilan. Konsekuensi utama yang mungkin terjadi adalah mengembalikan hasilnya dan melemparkan pengecualian, dan justru pada opsi-opsi inilah toolkit Mockito dirancang secara utama.


Dalam kasus paling sederhana yang sudah ditunjukkan di atas, respons terhadap panggilan adalah mengembalikan nilai. Saya akan memberikan kodenya lagi:


 List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data); 

Harap dicatat: Anda hanya dapat mengembalikan objek, tidak ada metode terpisah untuk primitif. Oleh karena itu, jika metode mengembalikan nilai primitif, dalam situasi seperti itu un / boxing akan terjadi. Dalam kebanyakan kasus, ini tidak mengganggu, tetapi jika kompilator berpikir sebaliknya, Anda harus setuju dengannya ... atau memasang peringatannya.


Melempar pengecualian tidak lebih sulit:


 Mockito.when(dataService.getDataById("invalidId")) .thenThrow(new IllegalArgumentException()); 

Ada cara lain: Anda bisa membuat objek pengecualian dan melemparkannya langsung, atau Anda bisa memberi Mockito hanya kelas pengecualian sehingga itu dibuat secara otomatis:


 Mockito.when(dataService.getDataById("invalidId")) .thenThrow(IllegalArgumentException.class); 

Dalam kedua kasus, sintaks memungkinkan Anda untuk menggunakan dan memeriksa pengecualian, tetapi Mockito tidak akan mengizinkan Anda untuk menjalankan tes seperti itu jika jenis pengecualian tidak cocok dengan metode yang saya ingin paksa melempar pengecualian ini.


Ketika menggunakan kelas sebagai parameter, konstruktor (bahkan tanpa parameter), serta inisialisasi bidang langsung, diabaikan - objek dibuat melewati mereka (setelah semua, ini Mockito!), Sehingga semua bidang pengecualian yang dilempar akan menjadi null . Oleh karena itu, jika konten pengecualian penting bagi Anda (misalnya, beberapa type bidang yang memiliki nilai default), Anda harus meninggalkan metode ini dan membuat pengecualian secara manual.


Opsi reaksi ini cocok jika, sebagai tanggapan terhadap panggilan dengan kondisi tertentu, Anda selalu perlu mengembalikan nilai tertentu, selalu hasil yang sama atau selalu membuang pengecualian yang sama, dan dalam kebanyakan kasus kemampuan ini cukup memadai. Tetapi bagaimana jika dibutuhkan lebih banyak fleksibilitas? Misalkan metode saya menerima kumpulan nilai, dan mengembalikan koleksi nilai lain yang terkait dengan yang pertama ke satu (misalnya, memperoleh kumpulan objek data dengan set ID mereka), dan saya ingin menggunakan objek tiruan ini berulang kali dengan set input yang berbeda dalam pengujian data, dapatkan setiap kali hasil yang sesuai. Anda dapat, tentu saja, menjelaskan reaksi secara terpisah untuk setiap set parameter tertentu, tetapi ada solusi yang lebih mudah - metode Mockito.thenAnswer , alias Mockito.then . Ia menerima implementasi antarmuka fungsional Answer , satu-satunya metode yang menerima objek kelas InvocationOnMock . Dari yang terakhir, saya dapat meminta parameter dari pemanggilan metode (satu dengan nomor atau sekaligus dalam bentuk array) dan bertindak dengan mereka sesuka saya. Misalnya, Anda bisa mendapatkan nilai yang sesuai untuk masing-masing elemen koleksi saya, membentuk koleksi baru dari mereka dan mengembalikannya (catatan: hasil yang diinginkan hanya dikembalikan, dan tidak ditulis ke beberapa bidang objek parameter, seperti yang Anda harapkan):


 Mockito.when(dataService.getDataByIds(Mockito.any())) .thenAnswer(invocation -> invocation .<List<String>>getArgument(0).stream() .map(id -> { switch (id) { case "a": return "dataItemA"; case "b": return "dataItemB"; default: return null; } }) .collect(Collectors.toList())); 

Secara ideologis, ini seperti menulis model metode nyata: mendapatkan parameter, memproses, mengembalikan hasil. - , - , , , mock- .


Answer , , — , AnswersWithDelay , ReturnsElementsOf . .


: InvocationOnMockObject[] , generic-.


thenCallRealMethod . . mock-, spy-. mock , , - null . spy thenCallRealMethod spy ; , - .


thenAnswer : InvocationOnMock callRealMethod() — , "" - .


OngoingStubbing OngoingStubbing , , , . , . thenReturn thenThrow , varargs. .


 Mockito.when(dataService.getDataById("a")) .thenReturn("valueA1", "valueA2") .thenThrow(IllegalArgumentException.class); 

"valueA1 , — "valueA2 ( ), ( ) IllegalArgumentException .



: (mock' ), . , : , , . verify .


, , :


 Mockito.verify(dataService).getDataById(Mockito.any()); 

, getDataById , , . , Mockito, when , , , mock-. , , , when , — mock', (. ).


:


 Mockito.verify(dataService, Mockito.times(1)) .getDataById(Mockito.any()); 

Mockito.times ; Mockito.never . Mockito.atLeast ( Mockito.atLeastOnce 1) Mockito.atMost , , Mockito.only , , mock- (. . ).


, Mockito , VerificationAfterDelay VerificationWithTimeout , Mockito.after Mockito.timeout . Sebagai contoh:


 Mockito.verify(dataService, Mockito.after(1000).times(1)) .getDataById(Mockito.any()); 

, mock , , , . . after timeout , , , — , . , timeout — . VerificationWithTimeout never atMost : .


, Mockito.any() . , , — Mockito , , . Mock- , , , , :


 dataService.getDataById("a"); dataService.getDataById("b"); Mockito.verify(dataService, Mockito.times(2)).getDataById(Mockito.any()); Mockito.verify(dataService, Mockito.times(1)).getDataById("a"); Mockito.verify(dataService, Mockito.never()).getDataById("c"); dataService.getDataById("c"); Mockito.verify(dataService, Mockito.times(1)).getDataById("c"); Mockito.verifyNoMoreInteractions(dataService); 

verifyNoMoreInteractions ( verifyZeroInteractions ) — - ( verify ) mock- — . : varargs, , , !


, , , . , InOrder :


 InOrder inOrder = Mockito.inOrder(dataService); 

varargs; — mock- , InOrder . verify , Mockito.verify :


 inOrder.verify(dataService, times(2)).saveData(any()); inOrder.verify(dataService).getData(); 

, saveData , getData . , InOrder , — .


, , — , . - , , — , , . ArgumentCaptor capture() . Sebagai contoh:


 DataSearchRequest request = new DataSearchRequest("idValue", new Date(System.currentTimeMillis()), 50); dataService.getDataByRequest(request); ArgumentCaptor<DataSearchRequest> requestCaptor = ArgumentCaptor.forClass(DataSearchRequest.class); Mockito.verify(dataService, times(1)).getDataByRequest(requestCaptor.capture()); assertThat(requestCaptor.getAllValues()).hasSize(1); DataSearchRequest capturedArgument = requestCaptor.getValue(); assertThat(capturedArgument.getId()).isNotNull(); assertThat(capturedArgument.getId()).isEqualTo("idValue"); assertThat(capturedArgument.getUpdatedBefore()).isAfterYear(1970); assertThat(capturedArgument.getLength()).isBetween(0, 100); 

ArgumentCaptor , , ArgumentCaptor . getValue() , getAllValues() — . , , .


Mock- Mockito


, mock- , — @Mock - :


 MockitoAnnotations.initMocks(this); 

( , mock', )


spy @Spy@Mock … spy , , ? , — spy .


@Captor ArgumentCaptor — , , .


@InjectMocks . - Mockito, . mock- , . , . - , null , - . ( ) dependency injection.


Mockito


, : mock (spy, argument captor...), , , . , mock' — , . JUnit , , TestNG — . , , mock' , , , . . , , — , .


, mock- . TestNG @BeforeMethod ( @AfterMethod ). mock' , , ( JUnit — @Before ).


, , — Mockito.reset Mockito.clearInvocations . varargs, mock'. , . : (, ) , / mock' , — . , mock' . . , , .


(, ) — MockitoAnnotations.initMocks(this); . "" , Mockito.


— Mockito. . mock- , ( mock' ). , MockitoSession , . TestNG:


 @Mock DataService dataService; MockitoSession session; @BeforeMethod public void beforeMethod() { session = Mockito.mockitoSession() .initMocks(this) .startMocking(); } @Test public void testMethod() { // some code using the dataService field } @AfterMethod public void afterMethod() { session.finishMocking(); } 

, — , "" (, ) , .


?


Mockito: mock spy-, . , . , , :


  • Mockito mock- MockSettings ( — , mock' - );
  • mock-, MockingDetails ;
  • BDDMockito Mockito ;
  • ( JUnit Mockito, ).

Mockito . javadoc' Mockito .


, , .

Source: https://habr.com/ru/post/id444982/


All Articles