【问题标题】:Mockito Inject mock into Spy objectMockito 将模拟注入 Spy 对象
【发布时间】:2017-10-11 02:50:51
【问题描述】:

我正在为具有 2 级依赖注入的类编写测试用例。我对 1 级依赖注入对象使用 @Spy 注释,我想模拟第 2 级注入。但是,我在第二级不断收到空指针异常。有什么方法可以将模拟注入@Spy 对象?

public class CarTestCase{
    @Mock
    private Configuration configuration;

    @Spy 
    private Engine engine;

    @InjectMocks 
    private Car car;

    @Test
    public void test(){

       Mockito.when(configuration.getProperties("")).return("Something");
       car.drive();
    }

}

public class Car{
    @Inject
    private Engine engine;

    public void drive(){
        engine.start();
    }
}

public class Engine{
    @Inject 
    private Configuration configuration;

    public void start(){
        configuration.getProperties();   // null pointer exception
    }

}

【问题讨论】:

  • 你用MockitoAnnotations.initmocks(this)初始化了模拟
  • 不,但这与这个问题有什么关系?
  • @Wildchild 很好,这使得 Mockito 可以对带有 @Spy@Mock@InjectMocks 等注释的对象执行应有的操作,因此如果您在构造 car 之后执行此操作@ 模拟应该是注入....
  • 我尝试将 MockitoAnnotations.initmocks(this) 放在测试函数的开头,但它仍然不起作用
  • 您是否也尝试使用 @InjectMocks 注释引擎?

标签: java unit-testing dependency-injection mockito


【解决方案1】:

我还徘徊过如何将模拟注入间谍。

以下方法将行不通

@Spy
@InjectMocks
private MySpy spy;

但是,当同时使用注释和手动模拟时,可以通过“混合”方法实现所需的行为。以下工作完美:

@Mock
private NeedToBeMocked needToBeMocked;

@InjectMocks
private MySpy mySpy;

@InjectMocks
private SubjectUnderTest sut;

@BeforeMethod
public void setUp() {
    mySpy = Mockito.spy(new MySpy());
    MockitoAnnotations.initMocks(this);
}

(这里SubjectUnderTest 依赖于MySpy,而MySpy 又依赖于NeedToBeMocked)。

UPD: 就个人而言,我认为如果你不得不经常做这样的魔法,这可能表明你的类之间的依赖关系有问题,值得执行进行一些重构以改进您的代码。

【讨论】:

  • 说实话,我不太明白。但它工作得很好:)
  • 明确地说,这行得通,但它似乎通过“魔法”起作用。我很高兴用它来解决我的短期问题(并赞成它),但我有一种不安的感觉,它可能随时中断:-)
  • @JPBelanger,是的,如果未来版本的 Mockito 改变他们创建模拟和间谍的方式,这种方法可能会中断。要查看目前它是如何工作的,您可以在 @Test 方法内的调试器断点处停止并查看 mySpy 变量内的内容。有一个 MySpy 对象的实例,它带有修改后的方法,这些方法可以做额外的事情,然后调用原始方法。所有字段都还在,可以通过@InjectMocks注入。
【解决方案2】:

Mockito 不能执行如此棘手的注入,因为它不是注入框架。因此,您需要重构代码以使其更具可测试性。使用构造函数注入很容易做到:

public class Engine{
    private Configuration configuration;

    @Inject 
    public Engine(Configuration configuration) {
        this.configuration = configuration;
    }
    ........
}

public class Car{
    private Engine engine;

    @Inject    
    public Car(Engine engine) {
        this.engine = engine;
    }
}

在这种情况下,您必须手动处理模拟和注入:

public class CarTestCase{

    private Configuration configuration;

    private Engine engine;

    private Car car;

    @Before
    public void setUp(){
        configuration = mock(Configuration.class);
        engine = spy(new Engine(configuration));
        car = new Car(engine);
    }

    @Test
    public void test(){

       Mockito.when(configuration.getProperties("")).return("Something");
       car.drive();
    }

}

【讨论】:

  • 我明白了,但是Spring框架中的一般设计是人们将注入放在构造函数中吗?那么注入注解的目的是什么?
  • @Wildchild 的工作原理相同。您不必显式调用构造函数,如果您在 Spring 上下文中,Spring 会为您执行此操作。而且,不推荐使用字段注入,最好使用构造函数注入。
【解决方案3】:

对我有用的(最简单的)解决方案。

@InjectMocks
private MySpy spy = Mockito.spy(new MySpy());

在这种情况下不需要MockitoAnnotations.initMocks(this),只要测试类用@RunWith(MockitoJUnitRunner.class)注释即可。

【讨论】:

    【解决方案4】:

    我在使用 Spring Boot 框架进行单元测试时也遇到了这个问题,但我找到了一种同时使用 @Spy 和 @InjectMocks 的解决方案

    Yoory N. 的先前回答

    @Spy
    @InjectMocks
    private MySpy spy;
    

    因为 InjectMocks 需要创建实例,所以适用于我的解决方案如下,

    @Spy
    @InjectMocks
    private MySpy spy = new MySpy();
    

    【讨论】:

    • 对我不起作用。我有 NotAMockException 使用版本 2.21.0 的 Mockito。在另一个具有古代 Mockito 1.10.19 的项目中,这不是按预期工作 - 注入了依赖项,但 spy 字段不是真正的间谍,我不能做像 Mockito.verify(mySpy).someMethodCall(); 这样的事情。
    • 为我工作。 JUnit 4.12,Mockito 1.10.19,我们还有 PowerMock 1.6.4
    • @ExtendWith(MockitoExtension.class) 一起使用时不起作用,您需要在@BeforeEach 设置方法中使用MockitoAnnotations.openMocks(this); 它才能工作乙>。 (使用 Mockito v3.12.4 和 junit-jupiter v5.8.1 测试)
    【解决方案5】:

    我想我刚刚找到了明确的答案。我尝试了 Yoory 方法,但更改了注释的顺序:

    @InjectMocks
    @Spy
    private MySpy spy;
    

    我假设 Mockito 首先创建了 mock,然后在其上添加了一个 spy。所以不需要实例化 MySpy 对象。

    【讨论】:

    • 不错的收获!这样spy 就是真正的 Mockito 间谍,所有字段都被注入。但是现在它无法使用@InjectMocks(如in my example)将此间谍注入SubjectUnderTest 实例,并且当它尝试调用间谍的方法时我得到NullPointerException。在 Mockito 2.21.0 上测试。还不知道如何克服这个问题。
    猜你喜欢
    • 2023-03-20
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多