【问题标题】:How to make Mockito throw an exception when a mock is called with non-defined parameters?当使用未定义的参数调用模拟时如何使 Mockito 抛出异常?
【发布时间】:2015-04-09 10:48:12
【问题描述】:

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

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

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

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

编辑 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));
  }
}

即使定义了adderMock.add(2, 3),也会引发异常。

【问题讨论】:

  • “是否可以在使用非预定义参数调用模拟时抛出异常?” - 这到底是什么意思?如果传递了某些参数,您想模拟抛出异常吗?非预定义参数是什么意思?
  • 因此,在 Mockito 中,您可以像这样定义模拟的返回值:when(myMock.someMethod(arg1, arg2)).thenReturn(result)。如果使用其他一些参数调用 myMock.someMethod,它会返回 null。我希望它立即失败并告诉我,该模拟是用参数调用的,我没有为其定义返回值。

标签: java mockito


【解决方案1】:

您可以在构造总是抛出异常的模拟时提供默认的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

【讨论】:

  • 谢谢。看起来这个Answer 可以重复使用并指定为@Mock(answer = thisAnswer)。我想知道为什么这不是mockito.Answers枚举中的选择之一。
  • 你可以使用现有的类org.mockito.internal.stubbing.answers.ThrowsException
  • 我对@Mock BTW 的看法是错误的。它只接受枚举值Answers,而不是任意的Answer
  • @OlegEterevsky 您需要如上所示手动创建模拟,而不是依赖注释。
  • @OlegEterevsky 好的,问题实际上来自您尝试存根方法时。要么使用doReturn().when(mock).method() 构造,要么在创建存根时打开/关闭标志并在Answer 中使用该标志。请参阅上面的更新。
【解决方案2】:

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

不管怎样,进入主题:)

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

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

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

【讨论】:

  • 我无意检查被测试的方法是否调用了某些东西。我只想快速找到我未指定的模拟中的调用。 RETURNS_SMART_NULLS 有帮助,但它不适用于 null 是合法返回值的方法。现在我正在调试这样一个问题。由于结果不正确,测试失败。我怀疑这是因为某些模拟方法返回了 null 而不是正确的值。
  • 您可以使用mock(Foo.class, withSettings().verboseLogging()); 打印所有交互 - 为了调试。此外,当您在测试中执行verify 时,如果验证失败,Mockito 将打印与给定对象的所有交互
  • 谢谢,记录可能会有所帮助。
  • '你设置(模拟)你的调用,你执行代码并验证交互是否完全符合你的预期'......为什么抛出异常而不是返回 null 与此相反?如果设置不正确或实现更改需要不同的设置,难道不应该考虑并明确说明而不是只返回 null 吗?让所有交互都进行显式更改,并且在出现回归时更加显式。
【解决方案3】:

只需使用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

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-25
    • 2018-07-02
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 1970-01-01
    相关资源
    最近更新 更多