620

@MockMockito 框架和@InjectMocksMockito 框架有什么区别?

4

12 回答 12

748

@Mock创建一个模拟。@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...

}
于 2013-05-09T17:46:20.287 回答
387

这是一个关于如何@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 类,即Playerand List<String>

于 2017-10-13T10:19:53.920 回答
87

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

@InjectMocks
private SomeManager someManager;

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

@Mock
private SomeDependency someDependency;

在此示例中,将模拟类SomeDependency内部。SomeManager

于 2016-03-06T16:03:57.587 回答
61

@Mock注释模拟相关对象。

@InjectMocks注释允许将由@Mock.

两者是互补的。

于 2013-05-09T17:46:54.963 回答
32
  • @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 框架教程

于 2018-01-13T19:28:42.133 回答
13

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

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

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

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

MockitoAnnotations.initMocks(this)调用,重置测试对象并重新初始化模拟,所以记住在你的@Before/@BeforeMethod注释中有这个。

于 2015-06-17T08:39:09.527 回答
10

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

   }

}
于 2018-11-21T12:32:17.423 回答
10

虽然上面的答案已经涵盖,但我只是尝试添加我认为缺少的微小细节。他们背后的原因(The Why)。

在此处输入图像描述


插图:

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

参考

于 2020-05-25T20:06:26.277 回答
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...

}

它是否是一种好的做法取决于您的应用程序设计。

于 2016-01-13T11:29:02.700 回答
8

@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"));
    }
于 2019-05-06T05:05:44.703 回答
6

很多人在这里对@Mockvs给出了很好的解释@InjectMocks。我喜欢它,但我认为我们的测试和应用程序应该以不需要使用@InjectMocks.

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

于 2018-08-16T06:37:09.217 回答
3

请注意,这@InjectMocks即将被弃用

弃用 @InjectMocks 并安排在 Mockito 3/4 中删除

您可以关注@avp 的回答并链接

为什么不应该使用 InjectMocks 注解来自动装配字段

于 2018-10-15T07:02:16.320 回答