我有一个类,它是Context
的子类。我正在对另一个依赖于该类的类进行单元测试,因此我嘲笑了它。但是,我需要一些方法来充当它们的原始行为,所以我要"取消模拟"它们。
其中之一是getAssets()
所以我写了这个,它工作正常:
Mockito.doReturn(this.getContext().getAssets()).when(keyboard).getAssets();
keyboard
是所提到的类的模拟实例。
由于此方法不带任何参数,因此覆盖它非常简单。
我也需要覆盖Context.getString(int)
。该参数使事情变得困难,并且它是原始的,使事情变得更加困难。
我接受了这个建议和另一个建议,并尝试编写以下代码:
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 中具有原始参数的方法?
提前致谢
你不能存根getString
因为它是最终的。 莫基托无法存根最终方法。 只需将其保留为非存根,您将获得其原始实现。 反正这就是你想要的,对吧?
不应该是Answer<String>
吗?
Mockito.when(keyboard.getString(Mockito.anyInt())).thenAnswer(new Answer<String>(){
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
return "foo";
}
});
你可以重命名键盘 ->mockContext,那会更清楚吗?
正如大卫·华莱士在他的回答中指出的那样,Context.getString()
方法是最终的,我们知道 mockito 不能模拟最终方法。所以覆盖它也是一个坏主意,行不通。
关于InvalidUseOfMatchersException
描述的"0 matchers expected, 1 recorded"
信息,是一种模糊的方式,表示该方法final
,我们不能试图覆盖它。
原样无济于事,因为该对象仍然是一个模拟对象,并且该方法不会显示我们希望从普通对象中获得的预期行为;因此此选项也不在桌面上。
最后,我决定改变和改进我的设计。我使用了接口隔离原则,并划分了Context
和Keyboard
的职责(我正在开发一个 IME 服务)。在应用程序本身中,我为这两个传递了相同的keyboard
对象,但在测试中,我模拟了没有任何不良副作用的Keyboard
,并使用TestCase.getContext()
作为Context
,消除了"取消模拟"其方法的需要。