如何在Mockito模拟上覆盖默认答案?

Dan*_*umb 11 java mockito

我有以下代码:

private MyService myService;

@Before
public void setDependencies() {
    myService = Mockito.mock(MyService.class, new StandardServiceAnswer());
    Mockito.when(myService.mobileMethod(Mockito.any(MobileCommand.class), Mockito.any(Context.class)))
            .thenAnswer(new MobileServiceAnswer());
}
Run Code Online (Sandbox Code Playgroud)

我的意图是,所有对被嘲笑者的召唤myService都应以标准方式回答.但是mobileMethod,应以特定方式回答(公开的)呼叫.

我发现的是,当我到达行添加调用的答案mobileMethod而不是附加时MobileServiceAnswer,Java实际上是在调用myService.mobileMethod,这会导致NPE.

这可能吗?看起来应该可以覆盖默认答案.如果有可能,这样做的正确方法是什么?

更新

这是我的Answers:

private class StandardServiceAnswer implements Answer<Result> {
    public Result answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();

        Command command = (Command) args[0];
        command.setState(State.TRY);

        Result result = new Result();
        result.setState(State.TRY);
        return result;
    }
}

private class MobileServiceAnswer implements Answer<MobileResult> {
    public MobileResult answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();

        MobileCommand command = (MobileCommand) args[0];
        command.setState(State.TRY);

        MobileResult result = new MobileResult();
        result.setState(State.TRY);
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jef*_*ica 27

两个无关的惊喜一起导致了这个问题:

在存根期间,Java会调用您的真实答案,并尝试调用setState基于matcher的(null)参数.基于Java评估顺序,这是有道理的:Mockito将您的答案称为被测系统调用您的答案,因为Mockito无法知道mobileMethod紧接呼叫之前的呼叫when.它还没有到达那里.

答案是使用"doVerb"的方法,比如doAnswer,doReturndoThrow,我喜欢称之为"尤达语法".因为这些包含when(object).method()而不是when(object.method()),Mockito有机会停用您之前设定的期望,并且您的原始答案永远不会被触发.它看起来像这样:

MyService myService = Mockito.mock(MyService.class, new StandardServiceAnswer());
Mockito.doAnswer(new MobileServiceAnswer())
    .when(myService).mobileMethod(
          Mockito.any(MobileCommand.class), Mockito.any(Context.class));
Run Code Online (Sandbox Code Playgroud)

值得注意的是,异常是您的覆盖不起作用的唯一原因.在正常情况下,"when-thenVerb"对于覆盖绝对没问题,并且会回溯先前的操作,以免甩掉连续的动作.thenReturn(...).thenThrow(...).同样值得注意的是,when(mobileMethod(command, context))如果没有抛出异常,它会发生变化command并且context在存根期间会发生微小的测试差距.

有些开发人员甚至比"when-thenVerb"语法更喜欢"doVerb-when"语法,因为它具有从不调用其他模拟的好行为.我们欢迎您得出相同的结论 - "doVerb"会执行"when-thenVerb"所做的一切,但在模拟和间谍中覆盖行为时更安全.我更喜欢"when"语法本身 - 它读起来更好一些,并且它会进行类型检查返回值 - 只要你记得有时候"doVerb"是获得你需要去的地方的唯一方法.