java 当使用未定义的参数调用模拟时,如何使 Mockito 抛出异常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29536418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:27:17  来源:igfitidea点击:

How to make Mockito throw an exception when a mock is called with non-defined parameters?

javamockito

提问by Oleg Eterevsky

Is it possible to throw an exception whenever a mock is called with non-predefined arguments? There is Answers.RETURNS_SMART_NULLS, but it's not really what I need, since it doesn't work if nullis legitimate return value, which doesn't lead to NullPointerException, but rather to errors later on.

是否可以在使用非预定义参数调用模拟时抛出异常?有Answers.RETURNS_SMART_NULLS,但它并不是我真正需要的,因为如果null返回值是合法的,它就不起作用,这不会导致NullPointerException,而是稍后导致错误。

Edit: some background. So, in Mockito when you define a mock, you specify the return values for each call like this:

编辑:一些背景。因此,在 Mockito 中,当您定义模拟时,您可以像这样为每个调用指定返回值:

when(myMock.someMethod(arg1, arg2)).thenReturn(returnValue);

When myMock.someMethodis called with arguments, for which I didn't give a return value in the test, it just returns null. I would like to configure it to crash right away and tell me that I forgot to define the return value for some combination of parameters.

myMock.someMethod使用参数调用时,我没有在测试中给出返回值,它只返回null. 我想将其配置为立即崩溃并告诉我我忘记为某些参数组合定义返回值。

Edit 2: There were suggestions to provide a custom defaultAnswerthat would throw exceptions when called. Unfortunately, this doesn't work. The default answers' answer()method is called even if a mock is present. Here's a sample:

编辑 2:有人建议提供defaultAnswer在调用时会抛出异常的自定义。不幸的是,这不起作用。answer()即使存在模拟,也会调用默认答案的方法。这是一个示例:

public class Test {
  public static class Adder {
    public int add(int a, int b) {
      return a + b;
    }
  }

  public static final Answer<Object> THROW_ON_UNDEFINED_ARGS = new Answer<Object>() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
      throw new IllegalArgumentException(
          String.format("Calling a mock with undefined arguments: %s %s",
              invocation.getMethod(),
              Arrays.toString(invocation.getArguments())));
    }
  };

  public static void main(String[] args) {
    Adder adderMock = mock(Adder.class, THROW_ON_UNDEFINED_ARGS);
    when(adderMock.add(2, 3)).thenReturn(5);
    System.out.println(adderMock.add(2, 3));
  }
}

The exception is thrown even though adderMock.add(2, 3)is defined.

即使adderMock.add(2, 3)已定义,也会抛出异常。

回答by Guillaume Polet

You could provide a default Answerin the construction of your mock that always throws an exception. Then every call that is stubbed will act like usual. Everything outside those paths will throw an exception. Something like this:

您可以Answer在构建模拟时提供一个总是抛出异常的默认值。然后每个被存根的呼叫都会像往常一样。这些路径之外的所有东西都会抛出异常。像这样的东西:

final String arg = "some arg";
Collection<Object> object = mock(Collection.class, new Answer<Object>() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        throw new IllegalArgumentException("You cannot invoke " + invocation.getMethod() +
                                    " with " + Arrays.toString(invocation.getArguments()));
    }
});
doReturn(true).when(object).add(arg);

object.add(arg); // Goes ok
object.add("azertyuiop"); // Throws the exception

回答by M4ks

First, a bit of "good engineering" mumble - why would you like to do this? Mockito tries to 'promote' BDD style - you set up (mock) your calls, you execute the code and verify interactions were exactly as you expected rather then 'it didn't called anything else' - Do you try to do something described in Finding irrelevant invocation? Generally if I want to mock all the cases, but one - this makes my ask myself whether my tests are really OK.

首先,有点“优秀的工程”喃喃自语——你为什么要这样做?Mockito 尝试“推广” BDD 风格——你设置(模拟)你的调用,你执行代码并验证交互完全符合你的预期,而不是“它没有调用任何其他东西”——你是否尝试做一些描述在寻找不相关的调用?一般来说,如果我想模拟所有情况,但有一个 - 这让我问自己我的测试是否真的没问题。

Anyway, to the topic :)

无论如何,进入主题:)

In Mockito, you can define multiple whens with different values, like

在 Mockito 中,您可以定义多个when具有不同值的 s,例如

class Foo {
   public String bar(int a) {
       return "bar = " + a;
   }
}

Mockito.when(task.bar(Matchers.anyInt())).thenReturn("L")
Mockito.when(task.bar(3)).thenThrow(new IllegalAccessError())

task.bar(4); // returns "L" 
task.bar(3); //throws IllegalAccessError

Notice that the order of whens DOES matter. The rules are processed in reversed order (or rather overrides the actual matchers). In my code we first mock for anyInt, then for 3 - which works. If you reverse it - both calls to bar()will return 'L'.

请注意,whens的顺序很重要。规则以相反的顺序处理(或者更确切地说是覆盖实际的匹配器)。在我的代码中,我们首先模拟 anyInt,然后模拟 3 - 这是有效的。如果您反转它 - 两次调用都bar()将返回“L”。

回答by troig

Just point another way you can do that, using thenAnswer:

只需指出另一种方法可以做到这一点,使用thenAnswer

when(myMock.someMethod(anyString(), anyString())).
            thenAnswer(new Answer<String>() {
               @Override
               public String answer(InvocationOnMock invocation) throws Throwable {
                  Object[] args = invocation.getArguments();
                  String arg1 = (String) args[0];
                  String arg2 = (String) args[1];

                  if ("arg1".equals(arg1) && "arg2".equals(arg2)) return "someValue";

                  throw new Exception();
               }
            });

      myMock.someMethod("arg1", "arg2"); // Returns "someValue"
      myMock.someMethod("xxx", "yyy");   // Throws Exception

Hope it helps.

希望能帮助到你。