我有以下代码:
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());
}
我的意图是,对模拟myService
的所有调用都应以标准方式回答。但是,应以特定方式回答mobileMethod
(公开)的电话。
我发现的是,当我到达该行以添加对mobileMethod
的调用的答案时,而不是附加MobileServiceAnswer
,Java实际上是在调用myService.mobileMethod
,这导致NPE。
这可能吗?看起来应该可以覆盖默认答案。 如果有可能,这样做的正确方法是什么?
以下是我的Answer
:
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;
}
}
答案 0 :(得分:22)
两个无关的惊喜一起导致了这个问题:
Mockito.any(Class)
doesn't actually return an object of that class。它返回null
并存储&#34;忽略参数并接受任何内容&#34;匹配secret internal matcher stack called ArgumentMatcherStorage。该参数值实际上是空的,但在大多数情况下你不会看到它。
声明when(foo.bar()).thenReturn(baz)
实际上始终会调用foo.bar()
。通常情况下,这没有任何副作用 - 特别是如果您将其第一个行动链断开 - 所以您没有注意到它。
在存根期间,Java会调用您的真实答案,并尝试在基于匹配器的(null)参数上调用setState
。基于Java评估顺序,这是有道理的:Mockito称你的答案就像是被测系统调用你的答案一样,因为Mockito没有办法知道对mobileMethod
的调用紧接在a之前致电when
。它还没有到达那里。
答案是使用&#34; doVerb&#34;方法,例如doAnswer
,doReturn
和doThrow
,我喜欢称之为&#34; Yoda语法&#34;。由于这些内容包含when(object).method()
而不是when(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));
值得注意的是例外是您的覆盖无效的唯一原因。在正常情况下&#34; when-thenVerb&#34;对于覆盖是绝对正确的,并且会回溯先前的操作,以免甩掉像.thenReturn(...).thenThrow(...)
这样的连续动作。同样值得注意的是when(mobileMethod(command, context))
在存根期间会更改command
和context
而不会抛出异常,这可能会带来微妙的测试差距。
有些开发人员甚至更喜欢&#34; doVerb-when&#34;语法超过&#34; when-thenVerb&#34;语法在任何时候,因为它具有从不调用其他模拟的好行为。欢迎你得出同样的结论 - &#34; doVerb&#34;做一切&#34; when-thenVerb&#34;但是,当在模拟和间谍中覆盖行为时使用更安全。我更喜欢&#34;当&#34;语法本身 - 它读得更好一些,并且它会对返回值进行类型检查 - 只要你记得有时&#34; doVerb&#34;是获得你需要去的地方的唯一途径。
答案 1 :(得分:-1)
您想要做的是有效的,当我这样做时,它有效:
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"));
}
因此,请使用调试器逐步执行测试用例,以找出与正常情况不同的内容。