关于java:@Mock和@InjectMocks之间的区别

Difference between @Mock and @InjectMocks

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


@Mock创建了一个模拟。@InjectMocks创建类的一个实例,并将用@Mock@Spy注释创建的模拟注入到这个实例中。

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

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

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

     //tests...

}


这是有关@Mock@InjectMocks工作方式的示例代码。

假设我们有GamePlayer两个班。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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方法模拟玩家类及其行为。最后,使用@InjectMocksmockito将把Player放入Game中。

注意,您甚至不必创建new Game对象。莫基托会给你注射。

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

我们还将使用@Spy注释获得相同的行为。即使属性名不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@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,即PlayerList


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

1
2
@InjectMocks
private SomeManager someManager;

从那时起,我们可以指定类中哪些特定的方法或对象将被mock替换,在这种情况下,SomeManager

1
2
@Mock
private SomeDependency someDependency;

在这个例子中,SomeManager类中的SomeDependency将被模拟。


@Mock注释模拟相关对象。

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

两者都是互补的。


  • @mock为您需要的类创建一个mock实现。
  • @injectmock创建类的一个实例,并将用annotations@mock标记的mock注入其中。

例如

1
2
3
4
5
6
7
8
9
10
@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

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

这里我们需要服务类的DAO类。所以,我们模拟它并将其注入到服务类实例中。同样,在Spring框架中,所有@autowired bean都可以被@mock在JUnits中模拟,并通过@injectmocks注入到bean中。

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

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


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

McCito是MIT许可下发布的一个Java开源测试框架,它是一个"嘲弄框架",它允许你用简洁简洁的API编写漂亮的测试。在Java空间中有许多不同的模拟框架,但是基本上有两种主要类型的模拟对象框架,它们是通过代理实现的,并且是通过类重新映射实现的。

依赖项注入框架(如Spring)允许您在不修改任何代码的情况下注入代理对象,模拟对象期望调用某个方法,它将返回预期的结果。

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

MockitoAnnotations.initMocks(this)调用,重置测试对象并重新初始化mock,因此请记住在@Before/@BeforeMethod注释中有这个。


@tom所提到的方法的一个优点是,您不必在somemanager中创建任何构造函数,从而限制客户机实例化它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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/


@InjectMocks注释可用于自动将模拟字段注入测试对象。

在下面的示例中,@injectmocks用于将模拟数据映射注入到数据库中。

1
2
3
4
5
6
7
8
9
10
11
12
13
@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"));
    }

@Mock用于声明/模拟依赖bean的引用,而@InjectMocks用于模拟为其创建测试的bean。

例如:

1
2
3
4
5
6
7
8
9
public class A{

   public class B b;

   public void doSomething(){

   }

}

A类试验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

注意,EDOCX1[1]即将被否决

deprecate @InjectMocks and schedule for removal in Mockito 3/4

您可以通过@avp answer and link访问:

Why You Should Not Use InjectMocks Annotation to Autowire Fields