В чем разница между @Mock
и @InjectMocks
в рамках Mockito?
В чем разница между @Mock
и @InjectMocks
в рамках Mockito?
Ответы:
@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...
}
Это пример кода о том, как @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
метод. И, наконец, использование @InjectMocks
Mockito внесет это 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>
.
В вашем тестовом классе тестируемый класс должен иметь пометку @InjectMocks
. Это говорит Mockito, в какой класс вводить макеты:
@InjectMocks
private SomeManager someManager;
С этого момента мы можем указать, какие конкретные методы или объекты внутри класса в этом случае SomeManager
будут заменены на mocks:
@Mock
private SomeDependency someDependency;
В этом примере SomeDependency
внутри SomeManager
класса будет издеваться.
@Mock
аннотация высмеивает заинтересованный объект.
@InjectMocks
аннотация позволяет вводить в базовый объект различные (и соответствующие) макеты, созданные @Mock
.
Оба дополняют друг друга.
@InjectMocks
для создания этого класса и шпионить за ним тоже.
Например
@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()
методе.
«Среда Mocking», на которой основан Mockito, - это среда, которая дает вам возможность создавать объекты Mock (в старых терминах эти объекты можно называть шунтами, поскольку они работают как шунты для зависимой функциональности) Другими словами, mock Объект используется для имитации реального объекта, от которого зависит ваш код, вы создаете прокси-объект с фреймворком. Используя фиктивные объекты в своих тестах, вы по сути переходите от обычного модульного тестирования к интеграционному тестированию.
Mockito - это среда тестирования с открытым исходным кодом для Java, выпущенная под лицензией MIT, это «среда разработки», которая позволяет писать прекрасные тесты с простым и понятным API. В пространстве Java существует множество различных фреймворков, однако, по сути, существует два основных типа фреймворков для фиктивных объектов: те, которые реализуются через прокси, и те, которые реализуются через переназначение классов.
Инфраструктуры внедрения зависимостей, такие как Spring, позволяют внедрять прокси-объекты без изменения какого-либо кода, фиктивный объект ожидает вызова определенного метода и возвращает ожидаемый результат.
@InjectMocks
Аннотацию пытается инициализировать объект тестирования экземпляра и впрыскивают поля аннотированных с @Mock
или @Spy
в частные поля объекта тестирования.
MockitoAnnotations.initMocks(this)
вызов, сбрасывает объект тестирования и повторно инициализирует макеты, так что не забудьте иметь это в вашей @Before
/ @BeforeMethod
аннотации.
Одно из преимуществ, которое вы получаете с подходом, упомянутым @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...
}
Хорошая практика или нет, зависит от вашего дизайна приложения.
Многие люди дали отличное объяснение здесь о @Mock
против @InjectMocks
. Мне это нравится, но я думаю, что наши тесты и приложения должны быть написаны таким образом, чтобы нам не нужно было их использовать @InjectMocks
.
Ссылка для дальнейшего чтения с примерами: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@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(){
}
}
Аннотация @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"));
}
Обратите внимание, что @InjectMocks
это должно быть устарело
устареть @InjectMocks и график удаления в Mockito 3/4
и вы можете следить за ответом @avp и ссылаться на:
Почему вы не должны использовать аннотацию InjectMocks для автоматического переноса полей
Хотя вышеупомянутые ответы покрыты, я только попытался добавить мелкие детали, которые я вижу отсутствующими. Причина, стоящая за ними (почему).
Иллюстрация:
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>
}
}