Apa perbedaan antara @Mock
dan @InjectMocks
dalam kerangka kerja Mockito?
Apa perbedaan antara @Mock
dan @InjectMocks
dalam kerangka kerja Mockito?
Jawaban:
@Mock
menciptakan tiruan. @InjectMocks
membuat instance kelas dan menyuntikkan tiruan yang dibuat dengan @Mock
(atau @Spy
) anotasi ke dalam instance ini.
Perhatikan bahwa Anda harus menggunakan @RunWith(MockitoJUnitRunner.class)
atau Mockito.initMocks(this)
menginisialisasi tiruan ini dan menyuntikkannya.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
Ini adalah kode sampel tentang cara @Mock
dan cara @InjectMocks
kerjanya.
Katakanlah kita memiliki Game
dan Player
kelas.
class Game {
private Player player;
public Game(Player player) {
this.player = player;
}
public String attack() {
return "Player attack with: " + player.getWeapon();
}
}
class Player {
private String weapon;
public Player(String weapon) {
this.weapon = weapon;
}
String getWeapon() {
return weapon;
}
}
Seperti yang Anda lihat, Game
kelas perlu Player
melakukan attack
.
@RunWith(MockitoJUnitRunner.class)
class GameTest {
@Mock
Player player;
@InjectMocks
Game game;
@Test
public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
assertEquals("Player attack with: Sword", game.attack());
}
}
Mockito akan mengejek kelas Player dan perilakunya menggunakan when
dan thenReturn
metode. Terakhir, menggunakan @InjectMocks
Mockito akan memasukkannya Player
ke dalam Game
.
Perhatikan bahwa Anda bahkan tidak perlu membuat new Game
objek. Mockito akan menyuntikkannya untukmu.
// you don't have to do this
Game game = new Game(player);
Kami juga akan mendapatkan perilaku yang sama menggunakan @Spy
anotasi. Bahkan jika nama atributnya berbeda.
@RunWith(MockitoJUnitRunner.class)
public class GameTest {
@Mock Player player;
@Spy List<String> enemies = new ArrayList<>();
@InjectMocks Game game;
@Test public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
enemies.add("Dragon");
enemies.add("Orc");
assertEquals(2, game.numberOfEnemies());
assertEquals("Player attack with: Sword", game.attack());
}
}
class Game {
private Player player;
private List<String> opponents;
public Game(Player player, List<String> opponents) {
this.player = player;
this.opponents = opponents;
}
public int numberOfEnemies() {
return opponents.size();
}
// ...
Itu karena Mockito akan memeriksa Type Signature
kelas Game, yaitu Player
dan List<String>
.
Di kelas tes Anda, kelas yang diuji harus diberi penjelasan @InjectMocks
. Ini memberitahu Mockito kelas mana yang akan diinjeksi:
@InjectMocks
private SomeManager someManager;
Sejak saat itu, kita dapat menentukan metode atau objek spesifik apa di dalam kelas, dalam hal ini SomeManager
, yang akan diganti dengan mengejek:
@Mock
private SomeDependency someDependency;
Dalam contoh ini, SomeDependency
di dalam SomeManager
kelas akan diejek.
@Mock
anotasi mengolok-olok objek yang bersangkutan.
@InjectMocks
anotasi memungkinkan untuk menyuntikkan ke objek yang mendasari berbagai (dan relevan) tiruan yang dibuat oleh @Mock
.
Keduanya saling melengkapi.
@InjectMocks
untuk membangun kelas ini dan memata-matai juga.
Sebagai contoh
@Mock
StudentDao studentDao;
@InjectMocks
StudentService service;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Di sini kita membutuhkan kelas DAO untuk kelas layanan. Jadi, kami mengejeknya dan menyuntikkannya dalam instance kelas layanan. Demikian pula, dalam kerangka Spring semua kacang @Autowired dapat diejek oleh @Mock di jUnits dan disuntikkan ke kacang Anda melalui @InjectMocks.
MockitoAnnotations.initMocks(this)
Metode menginisialisasi tiruan ini dan menyuntikkan mereka untuk setiap metode pengujian sehingga perlu disebut dalam setUp()
metode.
Tautan ini memiliki tutorial yang bagus untuk kerangka kerja Mockito
"Kerangka kerja mengejek", yang menjadi dasar Mockito, adalah kerangka kerja yang memberi Anda kemampuan untuk membuat objek Mock (dalam istilah lama objek-objek ini bisa disebut shunts, karena mereka berfungsi sebagai shunt untuk fungsionalitas dependend) Dengan kata lain, mock objek digunakan untuk meniru objek nyata yang menjadi sandaran kode Anda, Anda membuat objek proxy dengan kerangka kerja mengejek. Dengan menggunakan objek tiruan dalam pengujian Anda, Anda pada dasarnya beralih dari pengujian unit normal ke pengujian integrasi
Mockito adalah kerangka kerja pengujian sumber terbuka untuk Java yang dirilis di bawah Lisensi MIT, ini adalah "kerangka kerja mengejek", yang memungkinkan Anda menulis tes yang indah dengan API bersih dan sederhana. Ada banyak kerangka kerja mengejek yang berbeda di ruang Java, namun pada dasarnya ada dua jenis kerangka kerja objek tiruan, yang diimplementasikan melalui proxy dan yang diimplementasikan melalui kelas remapping.
Kerangka kerja injeksi ketergantungan seperti Spring memungkinkan Anda untuk menyuntikkan objek proxy Anda tanpa mengubah kode apa pun, objek tiruan mengharapkan metode tertentu untuk dipanggil dan itu akan mengembalikan hasil yang diharapkan.
The @InjectMocks
penjelasan mencoba untuk instantiate contoh dan menyuntikkan pengujian objek bidang dijelaskan dengan @Mock
atau @Spy
dalam bidang swasta dari objek pengujian.
MockitoAnnotations.initMocks(this)
panggilan, reset objek pengujian dan inisialisasi ulang mengejek, jadi ingatlah untuk memiliki ini di @Before
/ @BeforeMethod
anotasi Anda.
Satu keuntungan yang Anda dapatkan dengan pendekatan yang disebutkan oleh @Tom adalah bahwa Anda tidak harus membuat konstruktor di SomeManager, dan karenanya membatasi klien untuk membuat instantiate.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//You don't need to instantiate the SomeManager with default contructor at all
//SomeManager someManager = new SomeManager();
//Or SomeManager someManager = new SomeManager(someDependency);
//tests...
}
Apakah ini praktik yang baik atau tidak tergantung pada desain aplikasi Anda.
Banyak orang telah memberikan penjelasan yang besar di sini sekitar @Mock
vs @InjectMocks
. Saya suka, tapi saya pikir tes dan aplikasi kami harus ditulis sedemikian rupa sehingga kita tidak perlu menggunakannya @InjectMocks
.
Referensi untuk bacaan lebih lanjut dengan contoh: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@Mock
digunakan untuk mendeklarasikan / mengejek referensi kacang tergantung, sedangkan @InjectMocks
digunakan untuk mengejek kacang dimana tes sedang dibuat.
Sebagai contoh:
public class A{
public class B b;
public void doSomething(){
}
}
tes untuk kelas A
:
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
Penjelasan @InjectMocks dapat digunakan untuk menyuntikkan bidang tiruan ke dalam objek uji secara otomatis.
Dalam contoh di bawah ini, @InjectMocks telah digunakan untuk menyuntikkan mock dataMap ke dalam dataLibrary.
@Mock
Map<String, String> dataMap ;
@InjectMocks
DataLibrary dataLibrary = new DataLibrary();
@Test
public void whenUseInjectMocksAnnotation_() {
Mockito.when(dataMap .get("aData")).thenReturn("aMeaning");
assertEquals("aMeaning", dataLibrary .getMeaning("aData"));
}
Perhatikan bahwa @InjectMocks
akan segera ditinggalkan
deprecate @InjectMock dan jadwal untuk dihapus di Mockito 3/4
dan Anda dapat mengikuti @avp jawaban dan tautan di:
Mengapa Anda Tidak Harus Menggunakan Anotasi InjectMocks untuk Autowire Fields
Meskipun jawaban di atas telah dibahas, saya baru saja mencoba untuk menambahkan detail menit yang saya lihat hilang. Alasan di belakang mereka (The Why).
Ilustrasi:
Sample.java
---------------
public class Sample{
DependencyOne dependencyOne;
DependencyTwo dependencyTwo;
public SampleResponse methodOfSample(){
dependencyOne.methodOne();
dependencyTwo.methodTwo();
...
return sampleResponse;
}
}
SampleTest.java
-----------------------
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class})
public class SampleTest{
@InjectMocks
Sample sample;
@Mock
DependencyOne dependencyOne;
@Mock
DependencyTwo dependencyTwo;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
public void sampleMethod1_Test(){
//Arrange the dependencies
DependencyResponse dependencyOneResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyOne).methodOne();
DependencyResponse dependencyTwoResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyTwo).methodTwo();
//call the method to be tested
SampleResponse sampleResponse = sample.methodOfSample()
//Assert
<assert the SampleResponse here>
}
}