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

}

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


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.


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.


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


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.


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.


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


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(){

   }

}

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"));
    }

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


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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.