Perbedaan antara @Mock dan @InjectMocks

Jawaban:

542

@Mockmenciptakan tiruan. @InjectMocksmembuat 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...

}
Tom Verelst
sumber
2
Jawaban singkat dan ringkas. Bermanfaat juga;)
Chaklader Asfak Arefe
Apakah ini berfungsi untuk dependensi transitif atau hanya anggota langsung?
Pierre Thibault
@PierreThibault Mengejek mock hanya berfungsi untuk anggota langsung, tetapi Anda dapat mengatur mock untuk mengizinkan deep stubs static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Tom
1
saya merasa ini jauh lebih jelas daripada sebagian besar artikel online .... bahwa sedikit komentar menyelamatkanku ...
IHC_Applroid
Saya memiliki beberapa item yang tidak dapat disediakan oleh @Mock penjelasan seperti konteks. Bagaimana saya bisa menyediakannya untuk kelas utama?
Mahdi
220

Ini adalah kode sampel tentang cara @Mockdan cara @InjectMockskerjanya.

Katakanlah kita memiliki Gamedan Playerkelas.

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, Gamekelas perlu Playermelakukan 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 whendan thenReturnmetode. Terakhir, menggunakan @InjectMocksMockito akan memasukkannya Playerke dalam Game.

Perhatikan bahwa Anda bahkan tidak perlu membuat new Gameobjek. Mockito akan menyuntikkannya untukmu.

// you don't have to do this
Game game = new Game(player);

Kami juga akan mendapatkan perilaku yang sama menggunakan @Spyanotasi. 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 Signaturekelas Game, yaitu Playerdan List<String>.

aldok
sumber
16
Dengan contoh ini, itu harus menjadi jawaban yang diterima.
AnnaKlein
4
Saya pikir ini adalah asnwer terbaik juga
Evgeniy Dorofeev
4
Saya pikir ini adalah jawaban triple terbaik.
Harvey Dent
1
Kadang-kadang saya menemukan pengujian dengan mengejek sulit dimengerti dan desain untuk kelas. Namun, contoh ini sangat membantu dalam memberikan gambaran umum.
Chaklader Asfak Arefe
1
Terima kasih banyak :) Langsung ke titik dengan penjelasan yang lebih baik.
Rishi
80

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, SomeDependencydi dalam SomeManagerkelas akan diejek.

trdngy
sumber
6
apakah ini akan bekerja jika beberapa Manajer memiliki lebih dari satu konstruktor? bagaimana jika beberapa Manajer memiliki 5 konstruktor bagaimana ia tahu yang mana yang ingin Anda gunakan?
j2emanue
51

@Mock anotasi mengolok-olok objek yang bersangkutan.

@InjectMocksanotasi memungkinkan untuk menyuntikkan ke objek yang mendasari berbagai (dan relevan) tiruan yang dibuat oleh @Mock.

Keduanya saling melengkapi.

Mik378
sumber
1
Bisakah mereka digunakan bersama-sama pada objek yang sama?
IgorGanapolsky
1
Apakah Anda memiliki contoh mini dari kebutuhan Anda?
Mik378
Saya memiliki kelas yang perlu dimata-matai (melalui Mockito Spy), dan kelas ini memiliki konstruktor. Jadi saya berpikir untuk menggunakan @InjectMocksuntuk membangun kelas ini dan memata-matai juga.
IgorGanapolsky
1
Itukah yang kamu cari? stackoverflow.com/a/35969166/985949
Mik378
23
  • @Mock menciptakan implementasi tiruan untuk kelas yang Anda butuhkan.
  • @InjectMock membuat instance kelas dan menyuntikkan mock yang ditandai dengan penjelasan @Mock ke dalamnya.

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

Sana Jahan
sumber
13

"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 @InjectMockspenjelasan mencoba untuk instantiate contoh dan menyuntikkan pengujian objek bidang dijelaskan dengan @Mockatau @Spydalam bidang swasta dari objek pengujian.

MockitoAnnotations.initMocks(this)panggilan, reset objek pengujian dan inisialisasi ulang mengejek, jadi ingatlah untuk memiliki ini di @Before/ @BeforeMethodanotasi Anda.

serup
sumber
2
Saya tidak akan mengatakan bahwa "Dengan menggunakan benda tiruan dalam pengujian Anda, Anda pada dasarnya beralih dari pengujian unit normal ke pengujian integrasi". Bagi saya, mengejek adalah untuk mengisolasi fixture yang akan diuji untuk unit-test. Pengujian integrasi akan menggunakan dependensi nyata yang tidak diejek.
WesternGun
10

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.

tintin
sumber
bagaimana jika beberapa Manajer memiliki 3 konstruktor yang berbeda, bagaimana ia tahu mana yang digunakan?
j2emanue
Lalu bagaimana Anda memverifikasi barang-barang di Manajer tertentu jika tidak diejek?
IgorGanapolsky
5

Banyak orang telah memberikan penjelasan yang besar di sini sekitar @Mockvs @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/

rata-rata
sumber
1
Yang ini tampaknya menjadi solusi jangka panjang.
Vinayak Dornala
4

@Mockdigunakan untuk mendeklarasikan / mengejek referensi kacang tergantung, sedangkan @InjectMocksdigunakan 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(){

   }

}
dev_2014
sumber
4

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"));
    }
Lahiru Wijesekara
sumber
3

Perhatikan bahwa @InjectMocksakan 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

pengguna7294900
sumber
3

Meskipun jawaban di atas telah dibahas, saya baru saja mencoba untuk menambahkan detail menit yang saya lihat hilang. Alasan di belakang mereka (The Why).

masukkan deskripsi gambar di sini


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>
    }
}

Referensi

Angelin Nadar
sumber