Різниця між @Mock та @InjectMocks


Відповіді:


542

@Mockстворює макет. @InjectMocksстворює екземпляр класу та вводить у цей екземпляр макети, створені за допомогою @Mock(або @Spy) приміток.

Зауважте, що ви повинні використовувати @RunWith(MockitoJUnitRunner.class)або Mockito.initMocks(this)ініціалізувати ці макети та вводити їх.

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager

     //tests...

}

2
Коротка та лаконічна відповідь.
Корисно

Чи працює це для перехідних залежностей або лише прямих членів?
П'єр Тібо

@PierreThibault Ін'єкція макетів працює лише для прямих членів, але ви можете встановити макет, щоб дозволити глибокі заглушки static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Том Верелст,

1
я відчуваю, що це набагато зрозуміліше, ніж більшість статей в Інтернеті .... що маленькі коментарі врятують мою дупу ...
IHC_Applroid

У мене є деякі елементи, які не можуть надати @Mock анотацію, як контекст. Як я можу забезпечити це для основного класу?
Махді

220

Це зразок коду про те, як @Mockі як @InjectMocksпрацює.

Скажіть, у нас є Gameі Playerклас.

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

Як бачите, Gameкласу потрібно Playerвиконати 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 знущається з класу Player та його поведінки з використанням whenта thenReturnметодом. Нарешті, використання @InjectMocksMockito поставить це Playerв Game.

Зверніть увагу, що вам навіть не потрібно створювати new Gameоб’єкт. Мокіто буде вводити його вам.

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

Ми також отримаємо таку саму поведінку, використовуючи @Spyанотацію. Навіть якщо назва атрибута інша.

@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();
  }

  // ...

Це тому, що Mockito перевірить Type Signatureклас гри, який є Playerі List<String>.


16
На цьому прикладі має бути прийнята відповідь.
AnnaKlein

4
Я думаю, що це теж найкращий відповідь
Євгеній Дорофєєв,

4
Я думаю, що це найкраща відповідь потрійний.
Харві Дент

1
Іноді мені здається, що тестування з насмішкуватою важко зрозуміти та розробити для класу. Однак цей приклад дуже допомагає забезпечити огляд.
Chaklader Asfak Arefe

1
Велике спасибі :) До кращого пояснення.
Ріші

80

У вашому тестовому класі тестований клас слід зазначити @InjectMocks. Це говорить Mockito, в який клас вводити макети:

@InjectMocks
private SomeManager someManager;

З цього моменту ми можемо вказати, які конкретні методи чи об’єкти всередині класу, у цьому випадку SomeManager, будуть замінені макетами:

@Mock
private SomeDependency someDependency;

У цьому прикладі SomeDependencyвсередині SomeManagerкласу буде знущатися.


6
чи буде це працювати, якщо у деякогоManager більше одного конструктора? Що робити, якщо у деякихManager було 5 конструкторів, як би він знав, який з них ви хочете використовувати?
j2emanue

51

@Mock анотація висміює відповідний об’єкт.

@InjectMocksанотація дозволяє вставити в базовий об'єкт різні (і відповідні) макети, створені @Mock.

Обидва доповнюють один одного.


1
Чи можна їх використовувати в тандемі на одному об’єкті?
ІгорГанапольський

1
Чи є у вас міні-приклад вашої вимоги?
Mik378

У мене є клас, на який потрібно шпигувати (через Mockito Spy), і цей клас має конструктор. Тож я думав використати @InjectMocksдля побудови цього класу і шпигував за ним.
ІгорГанапольський

1
Це те, що ви шукаєте? stackoverflow.com/a/35969166/985949
Mik378

23
  • @Mock створює макетну реалізацію для потрібних вам класів.
  • @InjectMock створює екземпляр класу та вводить у нього макети, позначені анотаціями @Mock .

Наприклад

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

Тут нам потрібен клас DAO для класу обслуговування. Отже, ми знущаємось над ним і вводимо його в екземпляр класу обслуговування. Так само у весняній рамці всі @Autowired боби можна знущатись від @Mock в jUnits та вводити їх у квасолю через @InjectMocks.

MockitoAnnotations.initMocks(this)метод ініціалізує ці макети та вводить їх для кожного методу тестування, тому його потрібно викликати в setUp()методі.

Це посилання має хороший підручник для Mockito Framework


13

"Сміяльна рамка", на якій спирається Mockito, - це рамка, яка дає вам можливість створювати Mock об'єкти (по-старому, ці об'єкти можна назвати шунтами, оскільки вони працюють як шунти для залежних від функціональності) Іншими словами, макет Об'єкт використовується для імітації реального об'єкта, від якого залежить ваш код, ви створюєте проксі-об'єкт із глузливою рамкою. Використовуючи макетні об’єкти у своїх тестах, ви фактично переходите від звичайного одиничного тестування до інтеграційного тестування

Mockito - це тестова основа з відкритим кодом для Java, випущена під ліцензією MIT, це "глузлива рамка", яка дозволяє писати прекрасні тести з чистим та простим API. У просторі Java існує багато різних макетних фреймворків, однак, по суті, існує два основні типи фреймворків макетних об'єктів: реалізовані через проксі і ті, які реалізовані за допомогою перейменування класів.

Рамки введення залежностей, такі як Spring, дозволяють вводити свої проксі-об'єкти без зміни коду, макетний об’єкт очікує виклику певного методу, і він поверне очікуваний результат.

@InjectMocksАнотацію намагається ініціювати об'єкт тестування примірника і впорскують поля анотованих з @Mockабо @Spyв приватні поля об'єкта тестування.

MockitoAnnotations.initMocks(this)виклик, скидає тестуючий об’єкт та повторно ініціалізує макети, тому пам’ятайте, що це має у вашій @Before/ @BeforeMethodанотації.


2
Я б не сказав, що "Використовуючи макетні об'єкти у своїх тестах, ви по суті переходите від звичайного одиничного тестування до інтеграційного тестування". Для мене глузування - це ізолювати кріплення, яке підлягає випробуванню, для того, щоб зробити тест на пристрій. Інтеграційне тестування використовує реальні не знущаються залежності.
WesternGun

10

Однією з переваг, яку ви отримуєте від підходу, згаданого @Tom, є те, що вам не потрібно створювати будь-які конструктори в SomeManager, а отже, обмежувати клієнтів для його інстанції.

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

}

Чи є це гарною практикою чи ні, залежить від вашої конструкції програми.


Що робити, якщо у деякихManager були 3 різні конструктори, як би він знати, який з них використовувати?
j2emanue

Як ви потім перевіряєте речі на someManager, якщо вони не знущаються?
ІгорГанапольський

5

Багато людей дали велике пояснення тут про @Mockпроти @InjectMocks. Мені це подобається, але я думаю, що наші тести та заявки повинні бути написані таким чином, що нам не потрібно було використовувати @InjectMocks.

Довідка для подальшого читання з прикладами: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/


1
Це здається довгостроковим рішенням.
Vinayak Dornala

4

@Mockвикористовується для оголошення / знущання над посиланнями залежних бобів, тоді @InjectMocksяк використовується для знущання з квасолі, для якої створюється тест.

Наприклад:

public class A{

   public class B b;

   public void doSomething(){

   }

}

тест для класу A:

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

4

Анотацію @InjectMocks можна використовувати для автоматичного введення макетних полів у тестовий об’єкт.

Наведений нижче приклад @InjectMocks використовує для введення макету dataMap у бібліотеку даних.

@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

Хоча вищезгадані відповіді висвітлювались, я просто спробував додати хвилинні деталі, які я бачу відсутні. Причина за ними (The Why).

введіть тут опис зображення


Ілюстрація:

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

Довідково

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.