【问题标题】:Mockito - Overriding a method that takes primitive parametersMockito - 覆盖采用原始参数的方法
【发布时间】:2013-09-22 07:41:16
【问题描述】:

我有一个类是Context 的子类。我正在对另一个依赖于这个类的类进行单元测试,因此我嘲笑了它。但是,我需要一些方法来充当它们的原始行为,所以我要“取消模拟”它们。

其中一个是getAssets(),所以我写了这个并且它工作正常:

Mockito.doReturn(this.getContext().getAssets()).when(keyboard).getAssets();

keyboard 是上述类的模拟实例。

由于此方法不带参数,因此覆盖它非常简单。

我也需要覆盖Context.getString(int)。该参数使事情变得困难,它是一个原始的,使事情变得更加困难。

我拿了this advice 和另一个,并尝试编写这段代码:

Mockito.when(keyboard.getString(Mockito.anyInt())).thenAnswer(new Answer<String>(){
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable
         Integer arg = (Integer)invocation.getArguments()[0];
         return OuterClass.this.getContext().getString(arg.intValue());
    }
});

编译并执行,但出现以下异常:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at [...] <The code above>

This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));

For more info see javadoc for Matchers class.

at android.content.Context.getString(Context.java:282)
at [...]
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

所以主要问题是如何覆盖 Mockito 中具有原始参数的方法?

提前致谢

【问题讨论】:

  • 我无法重现您的错误。如果你搜索InvalidUseOfMatchersException,你会发现一堆相关的问题和答案。问题是您正在调用一个带有一个 Matcher 参数和一个没有的 Mockito 方法,由于各种原因,这是不允许的。
  • 感谢您的尝试。是的,我已经在搜索并且仍在搜索。

标签: java android unit-testing mockito


【解决方案1】:

你不能存根getString,因为它是最终的。 Mockito 不能存根 final 方法。只需保留它,你就会得到它的原始实现。这就是你想要的,对吧?

【讨论】:

  • 是的,我想要那个,但是保持原样,结果是 NullPointerException 里面的 getString()!我相信这是因为它调用了其他被存根的方法。这就是为什么我试图将它从MockedKeyboard.getString() 转发到AndroidTestCase.getContext.getString()。这不是正确的方法吗?
  • 您可以尝试使用默认答案CALLS_REAL_METHODS 创建一个模拟,然后只存根那些您真正想要存根的方法。所以Context mockContext = mock(Context.class, CALLS_REAL_METHODS);。如果其中一种方法使用尚未初始化的字段,您可能仍然会遇到麻烦。
  • 哦,谢谢伙计,我正在寻找这样的功能 :) 现在有帮助。
【解决方案2】:

不应该是Answer&lt;String&gt; 吗?

Mockito.when(keyboard.getString(Mockito.anyInt())).thenAnswer(new Answer<String>(){
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
         return "foo";
    }
});

你可以重命名键盘-->mockContext,这样会更清楚吗?

【讨论】:

  • 哦,是的,谢谢,我完全混淆了我的answer() 方法。我更正了,但是例外是一样的!
  • 真的很奇怪的错误,你确定没有其他模拟调用导致这个错误吗?
  • 当您使用混合参数创建模拟调用期望时会发生这种情况:Mockito.anyXXX 和真实参数(即“foo”)。
  • 是的,这很奇怪。我敢肯定,我禁用了所有测试,并且仅使用此设置代码进行的一个简单测试仍然存在,并且仍然是相同的错误。搜索互联网表明,尽管这个异常很清楚并用一个很好的例子进行了解释,但它在不相关的情况下显示出来,主要错误可能是其他的。
【解决方案3】:

正如his answer 中的David Wallace 所述,Context.getString() 方法是 final 的,我们知道 mockito 不能模拟 final 方法。所以覆盖它也是一个坏主意,并且不会起作用。

InvalidUseOfMatchersException 的描述中的"0 matchers expected, 1 recorded" 信息是一种模糊的说法,即方法是final,我们不能试图覆盖它。

但是保持原样并没有帮助,因为该对象仍然是一个模拟对象,并且该方法不会显示我们希望从普通对象获得的预期行为;所以这个选项也不在讨论范围内。

最后我决定改变和改进我的设计。我使用了Interface segregation principle,并划分了Context和Keyboard的职责(我正在开发IME服务)。在应用程序本身中,我为这两个传递了 keyboard 的相同对象,但在测试中我模拟了 Keyboard 没有任何不良副作用,并将 TestCase.getContext() 用作 Context 消除了“取消模拟”的需要" 它的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多