当前位置: 首页 > 知识库问答 >
问题:

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

益英逸
2023-03-14

我有以下代码:

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

我的意图是所有对mockedmyService的调用都应该以标准方式应答。但是对Mobile e方法(这是公共的)的调用应该以特定的方式应答。

我发现,当我添加对< code>mobileMethod调用的应答时,不是附加< code>MobileServiceAnswer,Java实际上是调用< code > my service . mobile method ,这导致了NPE。

这可能吗?似乎应该可以覆盖默认答案。如果可能,正确的方法是什么?

以下是我的< code >答案:

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个答案

唐博文
2023-03-14

你想做的是有效的,当我做的时候,它是有效的:

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

因此,使用调试器逐步执行测试用例,找出您所做的与这个简单用例不同的事情。

漆雕欣德
2023-03-14

两个不相关的意外一起导致了这个问题:

>

  • Mockito.any(class)实际上并不返回该类的对象。它返回null并将“忽略参数并接受任何内容”匹配器隐藏在一个名为ArgumentMatcher存储的秘密内部匹配器堆栈上。该参数值实际上为空,但在大多数情况下您不会看到它。

    语句当(foo.bar()). then返回(baz)实际上总是调用foo.bar()。通常这没有副作用——尤其是当你正在存根它的第一个动作链时——所以你不会注意到它。

    在存根期间,Java调用您的真实答案,并尝试在您的基于matcher的(空)参数上调用setState。根据Java评估顺序,这是有意义的:Mockito调用您的答案,就好像它是正在测试的系统调用您的答案一样,因为Mockito不可能知道当时,对移动方法的调用立即先于对的调用。它还没有到达那里。

    答案是使用“doVerb”方法,例如do的回答do返回,和doThrow,我喜欢称之为“尤达语法”。因为这些包含当(对象).方法()而不是当(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));
    

    值得注意的是,异常是您的覆盖不起作用的唯一原因。在正常情况下,“当时-thenVerb”完全可以覆盖,并且会回溯上一个操作,以免丢弃连续的操作,如. then的返回(…). thenThrow(…)。还值得注意的是,当(移动方法(命令,上下文))会在存根期间更改命令上下文而不会引发异常,这可能会引入微妙的测试空白。

    一些开发人员甚至在任何时候都更喜欢“doVerb-when”语法而不是“when-thenVerb”语法,因为它具有从不调用其他mock的良好行为。欢迎你得出同样的结论——“doVerb”在“thenVerb”做的时候做所有事情,但在模拟和间谍中重写行为时更安全。我自己更喜欢“when”语法,它读起来有点好,而且它确实会检查返回值,只要你记住有时“doVerb”是到达你需要的地方的唯一方法。

  •  类似资料:
    • 我有一个RESTAPI,我不想强迫客户端发送请求参数。我有将近400个api方法,我不想将所有参数设置为“required=false” 我想覆盖Spring RequestParam的默认行为。我想将RequestParam接口的“required”属性的默认值设置为“false”。 有什么方法可以覆盖它吗?如果我不能或这不是最佳实践,有什么方法可以解决上述问题。

    • 问题内容: 我已经能够覆盖所有名称以“ android:”为前缀的主题,但是Android themes.xml还定义了似乎无法被覆盖的属性。例如: colorTheground是在Theme.Light xml中定义的,但是在此处添加它可以使我 错误。如何为整个应用程序覆盖该样式? 问题答案: 您可以用修改属性(如)的方式覆盖标准属性,只是不要忘记添加如下前缀:

    • 问题内容: 我想覆盖Java外观。我只想显示不同的按钮。 我想要Windows Look and Feel的所有功能,但仅按钮有所不同。希望你明白我的意思。 还告诉我如何制作圆形的JtabbedPane ??? 问题答案: 自定义GUI类 调用您的自定义GUI类

    • 问题内容: 我有一个模板与此: Django自动将此翻译为Terminarsesión西班牙语。但是,我想将其翻译为Cerrarsesión。 我试图将此文字添加到.po文件中,但是在编译消息时出现错误,指出该文字重复。 有没有一种方法可以更改/覆盖默认的Django翻译? 谢谢。 问题答案: 最简单的方法是收集在django.contrib.admin语言环境文件夹中找到的.po文件,然后重新编

    • 我目前正在做一个项目,在这个项目中,我们有一个jenkins实例与一个单独的网站并排运行,工作流程是这样的,用户需要点击网站中的链接,从而触发jenkins实例中的jenkins作业。 触发的jenkins作业有几十个参数,我们目前正在做的是,该网站将使用javascript构建一个如下URL: https://JENKINS_HOST/作业/JOB_NAME/build with Paramet

    • 当它调用其他eureka客户机时使用feign。然而,当注册到eureka服务器时,它并不费力。我检查了源代码,发现eureka discovery客户端使用了jersey,jersey调用了http apache客户端。问题是,它使用了SchemeRegistryFactory.createDefault(),它将最终调用SSLContexts.createDefault(),后者不会考虑系统属