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

时间:2014-03-21 15:41:58

标签: 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());
}

我的意图是,对模拟myService的所有调用都应以标准方式回答。但是,应以特定方式回答mobileMethod(公开)的电话。

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

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

更新

以下是我的Answer

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

2 个答案:

答案 0 :(得分:22)

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

在存根期间,Java会调用您的真实答案,并尝试在基于匹配器的(null)参数上调用setState。基于Java评估顺序,这是有道理的:Mockito称你的答案就像是被测系统调用你的答案一样,因为Mockito没有办法知道对mobileMethod的调用紧接在a之前致电when。它还没有到达那里。

答案是使用&#34; doVerb&#34;方法,例如doAnswerdoReturndoThrow,我喜欢称之为&#34; Yoda语法&#34;。由于这些内容包含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));

值得注意的是例外是您的覆盖无效的唯一原因。在正常情况下&#34; when-thenVerb&#34;对于覆盖是绝对正确的,并且会回溯先前的操作,以免甩掉像.thenReturn(...).thenThrow(...)这样的连续动作。同样值得注意的是when(mobileMethod(command, context))在存根期间会更改commandcontext而不会抛出异常,这可能会带来微妙的测试差距。

有些开发人员甚至更喜欢&#34; doVerb-when&#34;语法超过&#34; when-thenVerb&#34;语法在任何时候,因为它具有从不调用其他模拟的好行为。欢迎你得出同样的结论 - &#34; doVerb&#34;做一切&#34; when-thenVerb&#34;但是,当在模拟和间谍中覆盖行为时使用更安全。我更喜欢&#34;当&#34;语法本身 - 它读得更好一些,并且它会对返回值进行类型检查 - 只要你记得有时&#34; doVerb&#34;是获得你需要去的地方的唯一途径。

答案 1 :(得分:-1)

您想要做的是有效的,当我这样做时,它有效:

private Properties props;

@Before 
public void setUp() {
    props = mock(Properties.class, new Answer<String>() {
        @Override
     public String answer(InvocationOnMock invocation) throws Throwable {
         return "foo";
     }
    } );
    when(props.get("override")).thenAnswer(new Answer<String>() {
        @Override
     public String answer(InvocationOnMock invocation) throws Throwable {
         return "bar";
     }
    } );
}

@Test
public void test() {
    assertEquals("foo", props.get("no override"));
    assertEquals("bar", props.get("override"));
}

因此,请使用调试器逐步执行测试用例,以找出与正常情况不同的内容。