手动实例化@InjectMock 注解字段

Manually instantiating the @InjectMock annotated field

为了了解 Mockito 注释工作原理的基础知识,我浏览了一些博客。

但是我对何时手动实例化用 @InjectMocks 注释的字段存在疑问,即

@InjectMocks
A a = new A();

什么时候依赖 MockitoAnnotations.initMocks() 功能来做同样的事情:

@InjectMocks
A a;

这取决于我们用于 运行 测试用例的 JunitTestRunner 还是取决于 Mockito 框架版本?

这取决于您是否正在使用(声明)跑步者。

如果您使用运行器,则无需自己调用 MockitoAnnotations.initMocks() - 运行器会为您调用。

通常我们会选择亚军。但是,当你想使用其他跑步者时(比如 Spring 的),你可以自己调用 .initMocks()

明确一点,MockitoAnnotations.initMocks(this) 将:

  • 实例化注释为@InjectMocks的字段
  • 为每个用 @Mock
  • 注释的字段创建一个模拟版本
  • @InjectMocks 变量的字段中注入 @Mocks(或调用其构造函数或使用其设置器 - 这取决于您使用哪种依赖注入)

Mockito runner、initMocks 和规则代码示例

下面的三个代码示例应该是等效的。

与亚军:

第一个代码片段使用了运行器,因此无需调用 initMocks()

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock private MyDependency myDependency;
    @InjectMocks private MyClass myClass;

    @Test
    public void myClass_should_get_stuff_from_dependency() {
        when(myDependency.getStuff()).thenReturn("stuff!");

        assertThat(myClass.getDependencyStuff(), is("stuff!"));
    }
}

没有跑步者+手动调用.initMocks():

另一个不使用运行器,因此需要 setUp() 方法调用我们的 initMocks() 朋友。

// notice there is no runner
public class MyClassTest {

    @Mock private MyDependency myDependency;
    @InjectMocks private MyClass myClass;

    // but now you have to call initMocks() yourself
    @Before
    public void setUp() throws Exception {
          MockitoAnnotations.initMocks(this);
    }

    @Test
    public void myClass_should_get_stuff_from_dependency() {
        when(myDependency.getStuff()).thenReturn("stuff!");

        assertThat(myClass.getDependencyStuff(), is("stuff!"));
    }
}

没有运行器或手动调用,使用 @Rule:

最后,正如, since version 1.10.17, there is also the possibility of using a JUnit @Rule called MockitoRule中指出的那样:

public class MyClassTest {

    @Rule
    public MockitoRule rule = MockitoJUnit.rule();

    @Mock private MyDependency myDependency;
    @InjectMocks private MyClass myClass;

    @Test
    public void myClass_should_get_stuff_from_dependency() {
        when(myDependency.getStuff()).thenReturn("stuff!");

        assertThat(myClass.getDependencyStuff(), is("stuff!"));
    }
}

感谢您的宝贵意见。 但是它仍然没有回答为什么要手动实例化用 @InjectMocks 注释的字段,而实例化应该通过调用 MockitoAnnotations.initMocks().

来处理的问题

尝试 运行 测试文件时出现以下错误:

Caused by: org.mockito.exceptions.base.MockitoException: Field 'student' annotated with @InjectMocks is null. Please make sure the instance is created before MockitoAnnotations.initMocks(); Example of correct usage:

class SomeTest {
    @InjectMocks private Foo foo = new Foo();

    @Before public void setUp() {
        MockitoAnnotations.initMock(this);

我进一步搜索,发现如果使用旧版本的 Mockito 框架,则会抛出错误。

http://myshittycode.com/category/testing/mockito/

一般来说,是否实例化带有 @InjectMocks 注释的对象的决定是代码风格的选择。在大多数情况下,不会有任何区别,因为 Mockito 旨在处理这两种情况。

但是,我在下面概述了一些差异。

@InjectMocks 将测试与构造函数的更改分离。

以同样的方式使用依赖注入框架将您的生产代码与对构造函数的更改分离开来。允许 Mockito 为您实例化 class 的实例,将您的测试代码与对构造函数的更改分离开来。这意味着将来可以对 class 构造函数进行任何更改,而不会在单元测试中导致编译错误。

我认为这是@InjectMocks最大的不同,也是最大的优势。

Mockito 将始终调用“最大”构造函数

注意:仅当您使用的代码不遵循最佳实践时,此差异才有意义。

当一个class中有多个构造函数时,Mocktio会调用参数最多的构造函数,即“最大”的构造函数。

这只会在以下情况下产生影响,

  1. “小”构造函数包含逻辑。
  2. 此逻辑是 class 正常运行所必需的。
  3. “最大”构造函数不会调用下一个“最小”构造函数。

这被认为是不好的做法,因为

  1. 应尽可能避免在构造函数中放置逻辑。
  2. 当一个 class 中有多个构造函数时,每个构造函数应首先调用它之前的构造函数。