When/how to use Mockito Answer
Monday, March 03, 2014
by Hongfei Ding, Software Engineer, Shanghai
Mockito is a popular open source Java testing framework that allows the creation of mock objects. For example, we have the below interface used in our SUT (System Under Test):
In our test, normally we want to fake the Service’s behavior to return canned data, so that the unit test can focus on testing the code that interacts with the Service. We use when-return clause to stub a method.
But sometimes you need mock object behavior that's too complex for when-return. An Answer object can be a clean way to do this once you get the syntax right.
A common usage of Answer is to stub asynchronous methods that have callbacks. For example, we have mocked the interface below:
Here you’ll find that when-return is not that helpful anymore. Answer is the replacement. For example, we can emulate a success by calling the onSuccess function of the callback.
Answer can also be used to make smarter stubs for synchronous methods. Smarter here means the stub can return a value depending on the input, rather than canned data. It’s sometimes quite useful. For example, we have mocked the Translator interface below:
We might choose to mock Translator to return a constant string and then assert the result. However, that test is not thorough, because the input to the translator function has been ignored. To improve this, we might capture the input and do extra verification, but then we start to fall into the “testing interaction rather than testing state” trap.
A good usage of Answer is to reverse the input message as a fake translation. So that both things are assured by checking the result string: 1) translate has been invoked, 2) the msg being translated is correct. Notice that this time we’ve used thenAnswer syntax, a twin of doAnswer, for stubbing a non-void method.
Last but not least, if you find yourself writing many nontrivial Answers, you should consider using a fake instead.
Mockito is a popular open source Java testing framework that allows the creation of mock objects. For example, we have the below interface used in our SUT (System Under Test):
interface Service {
Data get();
}
In our test, normally we want to fake the Service’s behavior to return canned data, so that the unit test can focus on testing the code that interacts with the Service. We use when-return clause to stub a method.
when(service.get()).thenReturn(cannedData);
But sometimes you need mock object behavior that's too complex for when-return. An Answer object can be a clean way to do this once you get the syntax right.
A common usage of Answer is to stub asynchronous methods that have callbacks. For example, we have mocked the interface below:
interface Service { void get(Callback callback); }
Here you’ll find that when-return is not that helpful anymore. Answer is the replacement. For example, we can emulate a success by calling the onSuccess function of the callback.
doAnswer(new Answer<Void>() { public Void answer(InvocationOnMock invocation) { Callback callback = (Callback) invocation.getArguments()[0]; callback.onSuccess(cannedData); return null; } }).when(service).get(any(Callback.class));
Answer can also be used to make smarter stubs for synchronous methods. Smarter here means the stub can return a value depending on the input, rather than canned data. It’s sometimes quite useful. For example, we have mocked the Translator interface below:
interface Translator {
String translate(String msg);
}
We might choose to mock Translator to return a constant string and then assert the result. However, that test is not thorough, because the input to the translator function has been ignored. To improve this, we might capture the input and do extra verification, but then we start to fall into the “testing interaction rather than testing state” trap.
A good usage of Answer is to reverse the input message as a fake translation. So that both things are assured by checking the result string: 1) translate has been invoked, 2) the msg being translated is correct. Notice that this time we’ve used thenAnswer syntax, a twin of doAnswer, for stubbing a non-void method.
when(translator.translate(any(String.class))).thenAnswer(reverseMsg()) ... // extracted a method to put a descriptive name private static Answer<String> reverseMsg() { return new Answer<String>() { public String answer(InvocationOnMock invocation) { return reverseString((String) invocation.getArguments()[0])); } } }
Last but not least, if you find yourself writing many nontrivial Answers, you should consider using a fake instead.
Here is another great use case for Answer: You can pass an Answer instance as second parameter to the mock method to define the default answer for all invocations on the returned mock object, which have not been stubbed.
ReplyDeleteExample: Suppose you want a pseudo object (a mock object, which throws an AssertionError whenever an unstubbed/unexpected method is called -- see "xUnit Test Patterns" by Gerard Meszaros) To easily construct a pseudo object you can use the following method:
public static T pseudo(Class clazz) {
return mock(clazz, new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw new AssertionError("Unexpected method call " + invocationOnMock);
}
});
}
Now you can write
Foo foo = pseudo(Foo.class);
in your test class to create a pseudo object of type Foo.
The only draw back here is, that you can not use the common stubbing style:
when(foo.method(...)).thenXxx(...);
because that leads to an AssertionError. To stub methods you need to write:
doXxx(...).when(foo).method(...);
@Unknown wouldn't that be the same as Mockito.verifyNoMoreInteractions(foo) ?
ReplyDelete