Mockito - Saya memahami mata-mata memanggil metode nyata pada suatu objek, sementara metode panggilan tiruan pada objek ganda. Mata-mata juga harus dihindari kecuali ada bau kode. Namun, bagaimana cara kerja mata-mata dan kapan saya harus menggunakannya? Bagaimana mereka berbeda dari tiruan?
102
Jawaban:
Secara teknis, baik "pengejek" dan "mata-mata" adalah jenis khusus dari "tes ganda".
Mockito sayangnya membuat perbedaannya menjadi aneh.
Maket dalam mockito adalah tiruan normal dalam framework tiruan lainnya (memungkinkan Anda menghentikan pemanggilan; yaitu, mengembalikan nilai tertentu dari panggilan metode).
Mata-mata di mockito adalah tiruan parsial dalam kerangka kerja tiruan lainnya (bagian dari objek akan diejek dan sebagian akan menggunakan pemanggilan metode nyata).
sumber
Keduanya dapat digunakan untuk meniru metode atau bidang. Perbedaannya adalah bahwa dalam tiruan, Anda membuat objek tiruan atau palsu lengkap sementara dalam mata-mata, ada objek nyata dan Anda hanya memata-matai atau menghentikan metode tertentu darinya.
Sementara di objek mata-mata, tentu saja, karena ini adalah metode nyata, ketika Anda tidak menghentikan metode tersebut, maka metode itu akan memanggil perilaku metode nyata. Jika Anda ingin mengubah dan mengejek metode ini, Anda perlu menghentikannya.
Perhatikan contoh di bawah ini sebagai perbandingan.
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MockSpy { @Mock private List<String> mockList; @Spy private List<String> spyList = new ArrayList(); @Test public void testMockList() { //by default, calling the methods of mock object will do nothing mockList.add("test"); Mockito.verify(mockList).add("test"); assertEquals(0, mockList.size()); assertNull(mockList.get(0)); } @Test public void testSpyList() { //spy object will call the real method when not stub spyList.add("test"); Mockito.verify(spyList).add("test"); assertEquals(1, spyList.size()); assertEquals("test", spyList.get(0)); } @Test public void testMockWithStub() { //try stubbing a method String expected = "Mock 100"; when(mockList.get(100)).thenReturn(expected); assertEquals(expected, mockList.get(100)); } @Test public void testSpyWithStub() { //stubbing a spy method will result the same as the mock object String expected = "Spy 100"; //take note of using doReturn instead of when doReturn(expected).when(spyList).get(100); assertEquals(expected, spyList.get(100)); } }
Kapan Anda harus menggunakan tiruan atau mata-mata? Jika Anda ingin aman dan menghindari panggilan layanan eksternal dan hanya ingin menguji logika di dalam unit, gunakan tiruan. Jika Anda ingin memanggil layanan eksternal dan melakukan panggilan ketergantungan nyata, atau hanya mengatakan, Anda ingin menjalankan program apa adanya dan hanya menghentikan metode tertentu, kemudian gunakan mata-mata. Jadi itulah perbedaan antara mata-mata dan tiruan di mockito.
sumber
TL; versi DR,
Dengan tiruan , ini menciptakan contoh cangkang tanpa tulang untuk Anda.
Dengan mata-mata, Anda dapat mengejek sebagian dari instance yang ada
List<String> spyList = Mockito.spy(new ArrayList<String>());
Kasus penggunaan tipikal untuk Spy: kelas memiliki konstruktor berparameter, Anda ingin membuat objek terlebih dahulu.
sumber
Saya telah membuat contoh yang dapat dijalankan di sini https://www.surasint.com/mockito-with-spy/
Saya menyalin sebagian di sini.
Jika Anda memiliki sesuatu seperti kode ini:
public void transfer( DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, double amount, String fromAccount, String toAccount) { withdrawMoneyService.withdraw(fromAccount,amount); depositMoneyService.deposit(toAccount,amount); }
Anda mungkin tidak perlu memata-matai karena Anda hanya dapat meniru DepositMoneyService dan WithdrawMoneyService.
Tetapi dengan beberapa kode lama, ketergantungan ada pada kode seperti ini:
public void transfer(String fromAccount, String toAccount, double amount) { this.depositeMoneyService = new DepositMoneyService(); this.withdrawMoneyService = new WithdrawMoneyService(); withdrawMoneyService.withdraw(fromAccount,amount); depositeMoneyService.deposit(toAccount,amount); }
Ya, Anda dapat mengubah ke kode pertama tetapi kemudian API diubah. Jika metode ini digunakan di banyak tempat, Anda harus mengubah semuanya.
Alternatifnya adalah Anda dapat mengekstrak dependensi seperti ini:
public void transfer(String fromAccount, String toAccount, double amount){ this.depositeMoneyService = proxyDepositMoneyServiceCreator(); this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator(); withdrawMoneyService.withdraw(fromAccount,amount); depositeMoneyService.deposit(toAccount,amount); } DepositMoneyService proxyDepositMoneyServiceCreator() { return new DepositMoneyService(); } WithdrawMoneyService proxyWithdrawMoneyServiceCreator() { return new WithdrawMoneyService(); }
Kemudian Anda dapat menggunakan mata-mata untuk menyuntikkan ketergantungan seperti ini:
DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class); WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class); TransferMoneyService target = spy(new TransferMoneyService()); doReturn(mockDepositMoneyService) .when(target) .proxyDepositMoneyServiceCreator(); doReturn(mockWithdrawMoneyService) .when(target) .proxyWithdrawMoneyServiceCreator();
Lebih detail ada di tautan di atas.
sumber
Tempat terbaik untuk memulai mungkin adalah dokumen untuk mockito .
Pada catatan umum, mockito mock memungkinkan Anda membuat rintisan.
Anda akan membuat metode rintisan jika, misalnya, metode tersebut melakukan operasi yang mahal. Katakanlah, ia mendapat koneksi database, mengambil nilai dari database dan mengembalikannya ke pemanggil. Mendapatkan koneksi db mungkin memerlukan waktu 30 detik, memperlambat eksekusi pengujian ke titik di mana Anda kemungkinan besar akan beralih konteks (atau berhenti menjalankan pengujian).
Jika logika yang Anda uji tidak peduli dengan koneksi database, maka Anda dapat mengganti metode itu dengan stub yang mengembalikan nilai hard code.
Mata-mata mockito memungkinkan Anda memeriksa apakah suatu metode memanggil metode lain. Ini bisa sangat berguna saat mencoba menguji kode lama.
Berguna jika Anda menguji metode yang bekerja melalui efek samping, maka Anda akan menggunakan mata-mata tiruan. Ini mendelegasikan panggilan ke objek nyata dan memungkinkan Anda untuk memverifikasi pemanggilan metode, berapa kali dipanggil, dll.
sumber
Pendeknya:
@Spy
dan@Mock
sering digunakan dalam pengujian kode, tetapi pengembang bingung dalam kasus kapan harus menggunakan salah satunya dan dengan demikian pengembang akhirnya menggunakan@Mock
agar aman.@Mock
jika Anda ingin menguji fungsionalitas secara eksternal tanpa benar-benar memanggil metode itu.@Spy
ketika Anda ingin menguji fungsionalitas secara eksternal + internal dengan metode yang dipanggil.Di bawah ini adalah contoh di mana saya telah mengambil skenario Election20xx di Amerika.
Pemilih dapat dibagi menurut
VotersOfBelow21
danVotersOfABove21
.Exit poll yang ideal mengatakan bahwa Trump akan memenangkan pemilihan karena
VotersOfBelow21
danVotersOfABove21
keduanya akan memilih truf dengan mengatakan " Kami memilih Presiden Trump "Tapi ini bukan skenario sebenarnya:
Jadi bagaimana cara mengujinya ??
public class VotersOfAbove21 { public void weElected(String myVote){ System.out.println("Voters of above 21 has no Choice Than Thrump in 20XX "); } }
public class VotersOfBelow21 { public void weElected(String myVote){ System.out.println("Voters of below 21 has no Choice Than Thrump in 20XX"); } }
public class ElectionOfYear20XX { VotersOfAbove21 votersOfAbove21; VotersOfBelow21 votersOfBelow21; public boolean weElected(String WeElectedTrump){ votersOfAbove21.weElected(WeElectedTrump); System.out.println("We elected President Trump "); votersOfBelow21.weElected(WeElectedTrump); System.out.println("We elected President Trump "); return true; } }
Sekarang Catatan di dua kelas pertama di atas, kedua kelompok usia orang mengatakan bahwa mereka tidak memiliki pilihan yang lebih baik daripada truf. Yang secara eksplisit berarti bahwa mereka memilih Trump hanya karena mereka tidak punya pilihan.
Sekarang
ElectionOfYear20XX
dikatakan bahwa Trump menang karena kedua kelompok umur sangat memilihnya.Jika kita Menguji
ElectionOfYear20XX
mengujinya dengan @Mock, maka kami mungkin tidak bisa mendapatkan alasan sebenarnya mengapa Trump menang, kami hanya akan menguji alasan eksternal.Jika kita mengujinya
ElectionOfYear20XX
dengan @Spy, maka kita mendapatkan alasan sebenarnya mengapa Trump menang dengan hasil exit poll eksternal, yaitu secara internal + eksternal.ELectionOfYear20XX_Test
Kelas kami :@RunWith(MockitoJUnitRunner.class) public class ELectionOfYear20XX_Test { @Mock VotersOfBelow21 votersOfBelow21; @Mock VotersOfAbove21 votersOfAbove21; @InjectMocks ElectionOfYear20XX electionOfYear20XX; @Test public void testElectionResults(){ Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice")); } }
Ini harus mengeluarkan hanya hasil tes logika yaitu pemeriksaan eksternal:
Menguji dengan
@Spy
eksternal maupun internal dengan pemanggilan metode aktual.@RunWith(MockitoJUnitRunner.class) public class ELectionOfYear20XX_Test { @Spy VotersOfBelow21 votersOfBelow21; @Spy VotersOfAbove21 votersOfAbove21; @InjectMocks ElectionOfYear20XX electionOfYear20XX; @Test public void testElectionResults(){ Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice")); } }
Keluaran:
Voters of above 21 has no Choice Than Thrump in 20XX We elected President Trump Voters of below 21 has no Choice Than Thrump in 20XX We elected President Trump
sumber
Saya suka kesederhanaan dari rekomendasi ini:
Sumber: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/
Perbedaan yang umum adalah:
sumber