@Mock和@InjectMocks之间的区别

use*_*972 404 java unit-testing mocking mockito

Mockito框架@Mock和之间有什么区别@InjectMocks

Tom*_*lst 494

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

}
Run Code Online (Sandbox Code Playgroud)

  • @PierreThibault注入模拟仅适用于直接成员,但您可以设置模拟以允许深层存根https://static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/Mockito.html#返回_DEEP_STUBS (6认同)
  • 这适用于传递依赖项还是仅适用于直接成员? (3认同)
  • ...现在`Mockito.initMocks(this)`已被弃用,应该使用`Mockito.openMocks(this)`。 (3认同)
  • 我觉得这比网上的大多数文章都清楚得多……那些小评论救了我的屁股…… (2认同)

ald*_*dok 165

这是关于如何@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如你所见,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());
    }

}
Run Code Online (Sandbox Code Playgroud)

Mockito将模拟一个Player类,它的行为使用whenthenReturn方法.最后,使用@InjectMocksMockito将把它Player放入Game.

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

// you don't have to do this
Game game = new Game(player);
Run Code Online (Sandbox Code Playgroud)

我们也将使用@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();
  }

  // ...
Run Code Online (Sandbox Code Playgroud)

那是因为Mockito会检查Type SignatureGame类,这是PlayerList<String>.

  • 通过这个例子,它应该是公认的答案. (10认同)
  • 我认为这也是最好的答案 (10认同)
  • 我认为这是最好的答案三倍。 (9认同)
  • 我有时会发现模拟测试很难理解和设计一个班级。但是,此示例对提供概述很有帮助。 (3认同)

小智 73

在测试类中,测试类应使用@InjectMocks进行注释.这告诉Mockito哪个类注入模拟:

@InjectMocks
private SomeManager someManager;
Run Code Online (Sandbox Code Playgroud)

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

@Mock
private SomeDependency someDependency;
Run Code Online (Sandbox Code Playgroud)

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

  • 如果someManager有多个构造函数,这会工作吗?如果someManager有5个构造函数怎么会知道你想要使用哪个呢? (6认同)

Mik*_*378 48

@Mock 注释嘲笑有关对象.

@InjectMocks注释允许向底层对象注入由其创建的不同(和相关)模拟@Mock.

两者都是互补的.


San*_*han 20

  • @Mock为您需要的类创建一个模拟实现.
  • @InjectMock 创建该类的实例,并将带有注释@Mock的模拟注入其中.

例如

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}
Run Code Online (Sandbox Code Playgroud)

这里我们需要服务类的dao类.因此,我们模拟它并将其注入服务类实例中.类似地,在Spring框架中,所有@Autowired bean都可以在jUnits中被@Mock模拟,并通过@InjectMocks注入到bean中.

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

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


小智 12

在此输入图像描述

@Mock用于创建和注入模拟实例,而无需手动调用 Mockito.mock。在此示例中,实例为ClassB

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


ser*_*rup 11

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

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

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

@InjectMocks注释尝试实例测试对象实例,并注入与注释字段@Mock@Spy进入测试对象的私有字段.

MockitoAnnotations.initMocks(this)调用,重置测试对象并重新初始化模拟,所以请记住在@Before/ @BeforeMethod注释中使用它.

  • 我不会说“通过在测试中使用模拟对象,您实际上将从普通的单元测试转向集成测试”。对我而言,模拟是为了进行单元测试而隔离要测试的灯具。集成测试将使用实际的非模拟依赖项。 (2认同)

dev*_*014 10

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

例如:

public class A{

   public class B b;

   public void doSomething(){

   }

}
Run Code Online (Sandbox Code Playgroud)

班级考试A

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}
Run Code Online (Sandbox Code Playgroud)


tin*_*tin 9

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

}
Run Code Online (Sandbox Code Playgroud)

是否良好实践取决于您的应用程序设计.


Ang*_*dar 9

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

在此处输入图片说明


插图:

Sample.java
---------------
    public class Sample{
        DependencyOne dependencyOne;
        DependencyTwo dependencyTwo;


        public SampleResponse methodOfSample(){
            dependencyOne.methodOne();
            dependencyTwo.methodTwo();

            ...

            return sampleResponse;
        }
    }
Run Code Online (Sandbox Code Playgroud)
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>
    }
}
Run Code Online (Sandbox Code Playgroud)

参考


Lah*_*ara 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"));
    }
Run Code Online (Sandbox Code Playgroud)


avp*_*avp 6

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

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