@Mock和@InjectMocks之间的区别


Answers:


541

@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/...
汤姆Verelst

1
我觉得这比网上的大多数文章都清楚得多。。。。那一点点评论就省了我的屁股……
IHC_Applroid

我有一些无法通过@Mock注释提供的项目,例如上下文。我如何为主要班级提供该课程?
Mahdi

220

这是一个关于如何一个示例代码@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将模拟Player类,并使用whenthenReturn方法对其行为进行模拟。最后,使用@InjectMocksMockito将其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将检查Type SignatureGame类的PlayerList<String>


16
对于此示例,它应该是公认的答案。
AnnaKlein '18 / 08/20

4
我认为这是最好的asnwer太
叶夫根尼·Dorofeev

4
我认为这是最好的答案。
哈维·邓特

1
有时,我很难理解和设计类的模拟测试。但是,此示例可以帮助您提供概述。
Chaklader Asfak Arefe,

1
非常感谢:)更好地解释了这一点。
Rishi

80

在您的测试班级中,被测试的班级应使用注释@InjectMocks。这告诉Mockito将模拟注入哪个类:

@InjectMocks
private SomeManager someManager;

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

@Mock
private SomeDependency someDependency;

在此示例中,将SomeDependencySomeManager类内部进行模拟。


6
如果someManager具有一个以上的构造函数,此方法将起作用吗?如果someManager有5个构造函数,它将如何知道您要使用哪一个?
j2emanue

51

@Mock 注释模拟相关对象。

@InjectMocks批注允许将所创建的不同(和相关)的模拟物注入到基础对象中@Mock

两者是互补的。


1
它们可以在同一物体上串联使用吗?
IgorGanapolsky

1
您有需求的小例子吗?
Mik378 '17

我有一个需要侦听的类(通过Mockito Spy),并且该类具有构造函数。因此,我正在考虑使用@InjectMocks该类来构造和监视它。
IgorGanapolsky

1
那是您要找的东西吗?stackoverflow.com/a/35969166/985949
Mik378 '17

23
  • @Mock为所需的类创建一个模拟实现。
  • @InjectMock 创建该类的实例,并将带有注释@Mock的模拟注入到该类中。

例如

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

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

在这里,我们需要DAO类作为服务类。因此,我们模拟它并将其注入服务类实例中。同样,在Spring框架中,所有@Autowired bean都可以由jUnits中的@Mock模拟,并通过@InjectMocks注入到您的bean中。

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

该链接为Mockito框架提供了很好的教程


13

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

Mockito是根据MIT许可证发布的Java开源测试框架,它是一个“模拟框架”,可让您使用简洁的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...

}

它的好作法取决于您的应用程序设计。


如果someManager具有3个不同的构造函数,怎么知道要使用哪个?
j2emanue'2

如果不模拟,如何在someManager上验证内容?
IgorGanapolsky


4

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

   }

}

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

尽管上面的答案已经涵盖了,但我只是想添加一些我看不见的小细节。他们背后的原因(为什么)。

在此处输入图片说明


插图:

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

参考

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.