@Mock和@InjectMocks的区别

Mockito框架中的@Mock@InjectMocks有什么区别?

496547 次浏览

@InjectMocks创建一个类的实例,并将使用@Mock(或@Spy)注释创建的模拟注入到该实例中。

请注意,您必须使用@RunWith(MockitoJUnitRunner.class)Mockito.initMocks(this)来初始化这些模拟并注入它们(JUnit 4)。

对于JUnit 5,您必须使用@ExtendWith(MockitoExtension.class)

@RunWith(MockitoJUnitRunner.class) // JUnit 4
// @ExtendWith(MockitoExtension.class) for JUnit 5
public class SomeManagerTest {


@InjectMocks
private SomeManager someManager;


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

// tests...


}

@Mock注释模拟相关对象。

@InjectMocks注解允许将@Mock创建的不同(和相关)模拟注入底层对象。

两者是互补的。

Mockito基于的“模拟框架”是一个框架,它使您能够创建Mock对象(用旧术语来说,这些对象可以称为分流,因为它们作为依赖功能的分流) 换句话说,模拟对象用于模拟您的代码所依赖的真实对象,您使用模拟框架创建一个代理对象。 通过在测试中使用模拟对象,您基本上是从普通的单元测试到集成测试

Mockito是MIT许可证下发布的Java的开源测试框架,它是一个“模拟框架”,可以让你用干净简单的API编写漂亮的测试。Java领域有许多不同的模拟框架,但是基本上有两种主要类型的模拟对象框架,一种是通过代理实现的,另一种是通过类重新映射实现的。

像Spring这样的依赖注入框架允许您在不修改任何代码的情况下注入代理对象,mock对象期望调用某个方法并返回预期结果。

@InjectMocks注释尝试实例化测试对象实例,并将@Mock@Spy注释的字段注入测试对象的私有字段。

MockitoAnnotations.initMocks(this)调用,重置测试对象并重新初始化模拟,因此请记住在@Before/@BeforeMethod注释中包含此内容。

使用@Tom提到的方法获得的一个优点是,您不必在某些管理器中创建任何构造函数,因此限制了客户端实例化它。

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


}

这是否是一个好的实践取决于您的应用程序设计。

在你的测试类中,测试类应该用@InjectMocks注释。这告诉Mockito将模拟注入哪个类:

@InjectMocks
private SomeManager someManager;

从那时起,我们可以指定类中的哪些特定方法或对象(在本例中为SomeManager)将被模拟替换:

@Mock
private SomeDependency someDependency;

在此示例中,SomeManager类中的SomeDependency将被模拟。

这是关于@Mock@InjectMocks如何工作的示例代码。

假设我们有GamePlayer类。

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将使用whenthenReturn方法模拟Player类及其行为。最后,使用@InjectMocks Mockito将Player放入Game

请注意,您甚至不必创建new Game对象。Mockito会为您注入它。

// 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将检查Game类的Type Signature,即PlayerList<String>

  • @吴兴龙为你需要的类创建一个模拟实现。
  • @陈志立创建类的一个实例并将标记有注释@吴兴龙的模拟注入其中。

例如

@Mock
StudentDao studentDao;


@InjectMocks
StudentService service;


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

这里我们需要服务类的DAO类。因此,我们模拟它并将其注入服务类实例中。 类似地,在Spring框架中,所有@陈志立 bean都可以通过jUnits中的@吴兴龙进行模拟,并通过@InjectMocks注入到您的bean中。

MockitoAnnotations.initMocks(this)方法初始化这些模拟并为每个测试方法注入它们,因此需要在setUp()方法中调用它。

这个链接有一个很好的Mockito框架教程

很多人在这里给出了关于@Mock@InjectMocks的很好的解释。我喜欢它,但我认为我们的测试和应用程序应该以这样一种方式编写,我们不应该使用@InjectMocks

参考示例进一步阅读:https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/

请注意,@InjectMocks即将成为已弃用

在Mockito 3/4中弃用@InjectMocks和删除时间表

您可以关注@avp答案与链接

为什么不应该使用InjectMocks注释来自动连接字段

@Mock用于声明/模拟依赖bean的引用,而@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用于将mock 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"));
}

虽然上面的答案已经涵盖了,但我只是试图补充一些我认为缺失的细节。他们背后的原因(为什么)。

在此处输入图片描述


插图:

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

参考

在此处输入图片描述

@Mock用于创建和注入模拟实例,而无需手动调用Mockito.mock。在这个例子中,实例将是ClassB

而@InjectMocks用于自动将模拟字段注入测试对象。在这种情况下,它将是ClassA