[英]How do I override default Answers on a Mockito mock?
I have the following code: 我有以下代码:
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());
}
My intention is that all calls to the mocked myService
should answer in a standard manner. 我的意图是对模拟myService
所有调用都应该以标准方式回答。 However calls to mobileMethod
(which is public) should be answered in a specific way. 但是,应以特定方式回答对mobileMethod
(公开)的调用。
What I'm finding is that, when I get to the line to add an answer to calls to mobileMethod
, rather than attaching the MobileServiceAnswer
, Java is actually invoking myService.mobileMethod
, which results in an NPE. 我发现的是,当我到达行添加对mobileMethod
调用的答案时,而不是附加MobileServiceAnswer
,Java实际上是在调用myService.mobileMethod
,这会导致NPE。
Is this possible? 这可能吗? It would seem like it should be possible to override a default answer. 看起来应该可以覆盖默认答案。 If it is possible, what is the correct way to do it? 如果有可能,这样做的正确方法是什么?
Here are my Answer
s: 这是我的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;
}
}
Two unrelated surprises are causing this problem together: 两个无关的惊喜一起导致了这个问题:
Mockito.any(Class)
doesn't actually return an object of that class . Mockito.any(Class)
实际上并不返回该类的对象 。 It returns null
and stashes a "disregard the parameter and accept anything" matcher on a secret internal matcher stack called ArgumentMatcherStorage . 它返回null
并在一个名为ArgumentMatcherStorage的秘密内部匹配器堆栈上隐藏 “忽略参数并接受任何东西”匹配器 。 That argument value will actually be null, but in most cases you won't see it. 该参数值实际上将为null,但在大多数情况下,您将看不到它。
The statement when(foo.bar()).thenReturn(baz)
actually calls foo.bar()
, always. when(foo.bar()).thenReturn(baz)
实际上总是调用foo.bar()
when(foo.bar()).thenReturn(baz)
的语句。 Typically this has no side effects—especially if you're stubbing its first chain of actions—so you don't notice it. 通常情况下,这没有任何副作用 - 特别是如果你正在对其第一个动作链进行存根 - 所以你没有注意到它。
During the stub, Java calls your real answer, and tries to call setState
on your matcher-based (null) argument. 在存根期间,Java会调用您的真实答案,并尝试在基于匹配器的(null)参数上调用setState
。 Based on Java evaluation order, this makes sense: Mockito calls your answer as if it were the system under test calling your answer, because there's no way for Mockito to know that the call to mobileMethod
immediately precedes a call to when
. 基于Java的计算顺序,这是有道理的:调用的Mockito你的答案好像是在测试调用你的答案的系统,因为没有办法来的Mockito知道调用mobileMethod
立即先于通话when
。 It hasn't gotten there yet. 它还没有到达那里。
The answer is to use the "doVerb" methods, such as doAnswer
, doReturn
, and doThrow
, which I like to call "Yoda syntax". 答案是使用“doVerb”方法,例如doAnswer
, doReturn
和doThrow
,我喜欢称之为“Yoda语法”。 Because these contain when(object).method()
instead of when(object.method())
, Mockito has a chance to deactivate your previously-set expectations, and your original answer is never triggered. 因为它们包含when(object).method()
而不是when(object.method())
,所以Mockito有机会停用你之前设定的期望,并且你的原始答案永远不会被触发。 It would look like this: 它看起来像这样:
MyService myService = Mockito.mock(MyService.class, new StandardServiceAnswer());
Mockito.doAnswer(new MobileServiceAnswer())
.when(myService).mobileMethod(
Mockito.any(MobileCommand.class), Mockito.any(Context.class));
It's worth noting that the exception is the only reason that your override didn't work . 值得注意的是, 异常是您的覆盖不起作用的唯一原因 。 Under normal circumstances "when-thenVerb" is absolutely fine for overriding, and will backtrack over the previous action so as not to throw off consecutive actions like .thenReturn(...).thenThrow(...)
. 在正常情况下,“when-thenVerb”绝对可以覆盖,并且会回溯前一个动作,以免甩掉像.thenReturn(...).thenThrow(...)
这样的连续动作。 It's also worth noting that when(mobileMethod(command, context))
would have changed command
and context
during the stub without throwing an exception, which can introduce subtle testing gaps. 值得注意的是, when(mobileMethod(command, context))
在存根期间更改command
和context
而不抛出异常时,这可能会引入细微的测试间隙。
Some developers go so far as to prefer the "doVerb-when" syntax over the "when-thenVerb" syntax at all times, because it has that nice behavior of never calling the other mock. 有些开发人员甚至比“when-thenVerb”语法更喜欢“doVerb-when”语法,因为它具有从不调用其他模拟的好行为。 You're welcome to come to the same conclusion—"doVerb" does everything "when-thenVerb" does, but is safer to use when overriding behavior in mocks and spies. 我们欢迎您得出相同的结论 - “doVerb”会执行“when-thenVerb”所做的一切,但在模拟和间谍中覆盖行为时更安全。 I prefer "when" syntax myself—it's a little nicer to read, and it does type-check return values—as long as you remember that sometimes "doVerb" is the only way to get where you need to go. 我更喜欢“when”语法本身 - 它读起来更好一些,并且它会进行类型检查返回值 - 只要你记得有时候“doVerb”是获得你需要去的地方的唯一方法。
What you want to do is valid, and when I do it it works: 你想做的是有效的,当我这样做时它有效:
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"));
}
So step through the execution of your testcase with a debugger to find out what you're doing that's different from this simple case. 因此,使用调试器逐步执行测试用例,以找出您正在做的与此简单案例不同的内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.