Разница между @Mock и @InjectMocks

Ответы:

545

@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 Инъекция mocks работает только для прямых участников, но вы можете установить mock, чтобы разрешить глубокие заглушки static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Tom Verelst
1
я чувствую, что это намного яснее, чем большая часть статьи в Интернете ... что небольшие комментарии спасают мою задницу ...
IHC_Applroid
У меня есть некоторые элементы, которые не могут быть предоставлены аннотацией @Mock, например контекст. Как я могу обеспечить это для основного класса?
Махди
221

Это пример кода о том, как @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класс Game, который есть Playerи List<String>.

aldok
источник
16
С этим примером это должен быть принятый ответ.
АннаКляйн
4
Я думаю, что это тоже лучший ответ
Евгений Дорофеев
4
Я думаю, что это лучший ответ тройной.
Харви Дент
1
Иногда я нахожу тестирование с трудным для понимания и надуманным дизайном для класса. Тем не менее, этот пример очень помогает обеспечить обзор.
Chaklader Асфак Арефе
1
Большое спасибо :) К вопросу с лучшим объяснением.
Риши
80

В вашем тестовом классе тестируемый класс должен иметь пометку @InjectMocks. Это говорит Mockito, в какой класс вводить макеты:

@InjectMocks
private SomeManager someManager;

С этого момента мы можем указать, какие конкретные методы или объекты внутри класса в этом случае SomeManagerбудут заменены на mocks:

@Mock
private SomeDependency someDependency;

В этом примере SomeDependencyвнутри SomeManagerкласса будет издеваться.

trdngy
источник
6
это будет работать, если у someManager больше одного конструктора? что если у someManager есть 5 конструкторов, как он узнает, какой из них вы хотите использовать?
j2emanue
51

@Mock аннотация высмеивает заинтересованный объект.

@InjectMocksаннотация позволяет вводить в базовый объект различные (и соответствующие) макеты, созданные @Mock.

Оба дополняют друг друга.

Mik378
источник
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 для класса обслуживания. Итак, мы высмеиваем это и внедряем это в экземпляр класса обслуживания. Точно так же в среде Spring все бины @Autowired могут быть смоделированы @Mock в jUnits и внедрены в ваш бин через @InjectMocks.

MockitoAnnotations.initMocks(this) Метод инициализирует эти макеты и вводит их для каждого тестового метода, поэтому его нужно вызывать в setUp() методе.

Эта ссылка имеет хороший учебник для фреймворка Mockito

Сана Джахан
источник
13

«Среда Mocking», на которой основан Mockito, - это среда, которая дает вам возможность создавать объекты Mock (в старых терминах эти объекты можно называть шунтами, поскольку они работают как шунты для зависимой функциональности) Другими словами, mock Объект используется для имитации реального объекта, от которого зависит ваш код, вы создаете прокси-объект с фреймворком. Используя фиктивные объекты в своих тестах, вы по сути переходите от обычного модульного тестирования к интеграционному тестированию.

Mockito - это среда тестирования с открытым исходным кодом для Java, выпущенная под лицензией MIT, это «среда разработки», которая позволяет писать прекрасные тесты с простым и понятным API. В пространстве Java существует множество различных фреймворков, однако, по сути, существует два основных типа фреймворков для фиктивных объектов: те, которые реализуются через прокси, и те, которые реализуются через переназначение классов.

Инфраструктуры внедрения зависимостей, такие как Spring, позволяют внедрять прокси-объекты без изменения какого-либо кода, фиктивный объект ожидает вызова определенного метода и возвращает ожидаемый результат.

@InjectMocksАннотацию пытается инициализировать объект тестирования экземпляра и впрыскивают поля аннотированных с @Mockили @Spyв частные поля объекта тестирования.

MockitoAnnotations.initMocks(this)вызов, сбрасывает объект тестирования и повторно инициализирует макеты, так что не забудьте иметь это в вашей @Before/ @BeforeMethodаннотации.

Serup
источник
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...

}

Хорошая практика или нет, зависит от вашего дизайна приложения.

Тинтин
источник
Что если у someManager есть 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/

AVP
источник
1
Это кажется долгосрочным решением.
Винаяк Дорнала
4

@Mock используется для объявления / макета ссылок зависимых бинов, в то время как @InjectMocks как используется для макета bean-компонента, для которого создается тест.

Например:

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

   }

}
dev_2014
источник
4

Аннотация @InjectMocks может использоваться для автоматической вставки фиктивных полей в тестовый объект.

В приведенном ниже примере @InjectMocks использовал для вставки фиктивного dataMap в 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

Обратите внимание, что @InjectMocksэто должно быть устарело

устареть @InjectMocks и график удаления в Mockito 3/4

и вы можете следить за ответом @avp и ссылаться на:

Почему вы не должны использовать аннотацию InjectMocks для автоматического переноса полей

user7294900
источник
3

Хотя вышеупомянутые ответы покрыты, я только попытался добавить мелкие детали, которые я вижу отсутствующими. Причина, стоящая за ними (почему).

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


Иллюстрация:

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

Ссылка

Красавица надар
источник