Suppose I have a code to test
void myMethod()
{
byte []data = new byte[1];
data[0]='a';
output.send(42, data);
data[0]='b';
output.send(55, data);
}
I write a test:
testSubject.myMethod();
verify(output).send(eq(42), aryEq(new byte[]{'a'}));
verify(output).send(eq(55), aryEq(new byte[]{'b'}));
The test will fail as the method implementation reuses the same array for both calls, it is impossible to match args of the first send invocation after method finishes, so techically the verify statements should be specified before the method call, something like an expectation.
What is the right way to test such methods?
Well it looks like Mockito is a bit inconvenient here. It detects the method call and logs it (use mock(MyOutput.class, withSettings().verboseLogging()); to enable logging), but it stores the reference to the array you're passing and thus is influenced when you mutate the array. It then thinks the method call was send(42, [98]) rather than send(42, [97]).
A possible way to work with that is to use the expectations you've mentioned. For example you can use a counter and increment it if a call matched the expectations (it is really just a workaround and rather nasty):
MyOutput mock = mock(MyOutput.class, withSettings().verboseLogging());
Main subject = new Main(mock);
AtomicInteger correctCallsCounter = new AtomicInteger(0);
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(55), aryEq(new byte[]{'b'}));
subject.myMethod();
assertThat(correctCallsCounter.get(), is(2));
It works, because doAnswer is triggered when the call happens and when the byte array hasn't been changed yet.
The big downside of this workaround is, that it only works with void methods. If send would return "something", then I currently don't see a way to work around that.
Well and the other one is that this is obviously a rather nasty workaround.
So I would suggest to refactor your code a bit (use a new array) if that is possible. That would avoid these issues here.
If your send method would indeed return something and your myMethod method would rely on it, then you would usually mock it this way (send is expected to return a String in this example):
when(mock.send(eq(55), aryEq(new byte[]{'b'}))).thenReturn("something");
In order to still use the above mentioned workaround, you could change the doAnswer method to increment your counter and return your String (which you would mock anyway, thus it isn't that bad):
doAnswer(invocation -> {
correctCallsCounter.incrementAndGet();
return "something";
}).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
Use an Answer to copy the value of the parameter.
Here is some code (it is not pretty):
public class TestMyClass
{
private static List<byte[]> mockDataList = new ArrayList<>();
#InjectMocks
private MyClass classToTest;
private InOrder inOrder;
#Mock
private ObjectClass mockOutputClass;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockOutputClass);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doAnswer(new Answer()
{
#Override
public Object answer(
final InvocationOnMock invocation)
throws Throwable
{
final byte[] copy;
final byte[] source = invocation.getArgument(1);
copy = new byte[source.length];
System.arraycopy(source, 0, copy, 0, source.length);
mockDataList.add(copy);
return null;
}
}).when(mockOutputClass).send(anyInt(), any(byte[].class));;
inOrder = inOrder(
mockOutputClass);
}
#Test
public void myMethod_success()
{
byte[] actualParameter;
final byte[] expectedFirstArray = { (byte)'a' };
final byte[] expectedSecondArray = { (byte)'b' };
classToTest.myMethod();
actualParameter = mockDataList.get(0);
assertArrayEquals(
expectedFirstArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(42), any(byte[].class));
actualParameter = mockDataList.get(1);
assertArrayEquals(
expectedSecondArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(55), any(byte[].class));
}
}
Note that the value of the parameter is compared separate of the verification of the call, but the order of the parameters is still verified (i.e. 'a' array first, then 'b' array).
Related
I've to write test for a class that calls an API and then processes the response. The class has two public functions and a private function. The first public method fetches a list of IDs. The second public method is called in a loop for every ID to get details associated with an ID. The private method is called inside the second public method, since the calls to fetch details based on id are made asynchronously.
I'm new to JUnits and while I understand that I should not test the API calls, just my functions, I still don't understand what should the unit tests assert.
Below are my functions:
public List<Integer> fetchVehicleIds(String datasetId) throws ApiException {
VehiclesApi vehiclesApi = new VehiclesApi();
List<Integer> vehicleIds;
vehicleIds = vehiclesApi.vehiclesGetIds(datasetId).getVehicleIds();
return vehicleIds;
}
public List<VehicleResponse> fetchVehicleDetails(String datasetId, List<Integer> vehicleIds) throws InterruptedException, ApiException {
CountDownLatch latch = new CountDownLatch(vehicleIds.size());
List<VehicleResponse> vehiclesList = new ArrayList<>();
for (Integer vehicleId: vehicleIds) {
populateEachVehicleDetail(datasetId, vehicleId, vehiclesList, latch);
}
latch.await();
return vehiclesList;
}
private void populateEachVehicleDetail(String datasetId, Integer vehicleId, List<VehicleResponse> vehiclesList, CountDownLatch latch) throws ApiException {
ApiCallback<VehicleResponse> vehicleResponseApiCallback = new ApiCallback<VehicleResponse>() {
#Override
synchronized public void onSuccess(VehicleResponse result, int statusCode, Map<String, List<String>> responseHeaders) {
vehiclesList.add(result);
latch.countDown();
}
};
VehiclesApi vehiclesApi = new VehiclesApi();
vehiclesApi.vehiclesGetVehicleAsync(datasetId,vehicleId,vehicleResponseApiCallback);
}
Based on the research I've done so far, I think I have to mock the API calls using mockito? I'm still unclear on how the functionality can be unit tested.
These two statements are indeed the things that you want to isolate in your unit test:
private void populateEachVehicleDetail(String datasetId, Integer vehicleId, List<VehicleResponse> vehiclesList, CountDownLatch latch) throws ApiException {
....
VehiclesApi vehiclesApi = new VehiclesApi();
vehiclesApi.vehiclesGetVehicleAsync(datasetId,vehicleId,vehicleResponseApiCallback);
...
}
1) Make you dependency mockable
But you can mock only something that you can set from the client side of the class.
Here the API is a local variable. So you should change your class to expose the dependency, for example in the constructor.
In this way you could mock it easily.
2) Make your mock not return a result but invoke the callback.
In a synchronous invocation context, you want to mock a returned result.
In an asynchronous invocation context with a callback, things are different. Indeed callbacks don't return to the caller but callbacks are invoked to provide the result of the invocation.So here what you want is that the mocked API invokes the onSuccess() callback with mocked parameters that represent the data set for your unit test :
#Override
synchronized public void onSuccess(VehicleResponse result, int statusCode, Map<String, List<String>> responseHeaders) {
vehiclesList.add(result);
latch.countDown();
}
In your unit test you should mock in this way the callback for each expected invocation :
#Mock
VehiclesApi vehiclesApiMock;
// ...
// when the api method is invoked with the expected dataSetId and vehicleId
Mockito.when(vehiclesApiMock.vehiclesGetVehicleAsync(Mockito.eq(datasetId), Mockito.eq(vehicleId),
Mockito.any(ApiCallback.class)))
// I want to invoke the callback with the mocked data
.then(invocationOnMock -> {
ApiCallback<VehicleResponse> callback = invocationOnMock.getArgument(2);
callback.onSuccess(mockedVehicleResponse, mockedStatusCode,
mockedResponseHeaders);
return null; // it is a void method. So no value to return in T then(...).
});
I think that a cast is missing for ApiCallback but you should have the overall idea.
You are right: since you want to test your unit (i.e. the presented code), you should mock the API (mainly: the vehicleApi instance).
As-is right now, there is no way to inject a mocked instance of VehicleApi in your code (well, there is, but it would involve use of reflection... let's not go down this road). You can apply Inversion of Control to make your code testable: instead of constructing a VehicleApi within your object, write a constructor that expects a VehicleApi-instance:
public class YourClass {
private final VehicleApi vehicleApi;
public YourClass(final VehicleApi vehicleApi) {
this.vehicleApi = vehicleApi;
}
[...]
}
What have you won? Well, now you can inject a mocked object into your unit under test:
#RunWith(MockitoJRunner.class)
public class YourClassTest {
private final VehicleApi vehicleApiMock = mock(VehicleApi.class);
private final YourClass underTest = new YourClass(vehicleApiMock);
#Test
void someTest() {
// GIVEN
[wire up your mock if necessary]
// WHEN
[write the test-call]
// THEN
[verify that the unit under test is in the expected state]
}
}
This example assumes JUnit5 as testing- and Mockito as mocking-framework, but there are other options as well.
The test is written in Gherkin language:
- the GIVEN block describes the preconditions, i.e. in which the unit under test and the external (mocked) system(s) are in
- the WHEN block executes the action that should be tested
- the THEN block validates that the unit under test is in the expected state.
I have a JUnit test as:
#Spy
ParallelSender parallelSender = new ParallelSender();
#Test
public void send() {
// making some data...
parallelSender.send(someData);
// check that internal method has been called with Sender Task made from someData
verify(parallelSender).doSend(any(SenderTask.class));
}
I however like to examine that SenderTask contains all the fields exactly as I need them. Can I tell the spy to intercept the doSend call, store its parameters in some array and then continue to real method?
Use the ArgumentCaptor:
#Test
public void send() {
// making some data...
parallelSender.send(someData);
// Define the captor for class
ArgumentCaptor<SenderTask> captor =
ArgumentCaptor.forClass(SenderTask.class);
// Capture input while verifying
verify(parallelSender).doSend(captor.capture());
// Assert
SomeTask result = captor.getValue();
// assertions on result
}
You can use an ArgumentCaptor.
#Captor
ArgumentCaptor<SenderTask> captor;
// or ArgumentCaptor<SenderTask> captor =
// ArgumentCaptor.forClass(SenderTask.class);
#Test public void send() {
// ...
verify(parallelSender).doSend(captor.capture());
SenderTask captured = captor.getValue();
I rarely use argument captor because it's usually not necessary.
Just do this
#Test
public void send() {
//given
SomeData myInput = ...
SenderTask expectedOutput = new SenderTask();
expectedOutput.setSomeField(/*expected field value*/);
//when
parallelSender.send(myInput);
//then
verify(parallelSender).doSend(expectedOutput);
}
The idea behind is to check that "doSend" was called with an expected object.
Note: just make sure that you implemented equals/hash method in SenderTask - or it will not work
Note2: I would suggest avoiding using any() in your unit tests. Usually when your are unit-testing something - you want to be as much precise as possible. So use concrete objects during results verification.
Hopefully it helps
I have to test a method which uses a mutable object
private final List<LogMessage> buffer;
...
flushBuffer() {
sender.send(buffer);
buffer.clear();
}
I need to test that it sends buffers with exact size.
ArgumentCaptor is not applicable because the captured collection is clear by the time of assertion.
Is there a kind of matcher which can reuse Hamcrest's hasSize() and does check right in time of method call?
I would prefer something like this hypothetical collectionWhich matcher:
bufferedSender.flushBuffer();
verify(sender).send(collectionWhich(hasSize(5)));
A lightweight alternative to David's idea: Use an Answer to make a copy at the time of the call. Untested code, but this should be pretty close:
final List<LogMessage> capturedList = new ArrayList<>();
// This uses a lambda, but you could also do it with an anonymous inner class:
// new Answer<Void>() {
// #Override public Void answer(InvocationOnMock invocation) { /* ... */ }
// }
when(sender.send(any())).thenAnswer(invocation -> {
List<LogMessage> argument = (List<LogMessage>) invocation.getArguments()[0];
capturedList.addAll(argument);
});
bufferedSender.flushBuffer();
assertThat(capturedList).hasSize(5);
The Jeff Bowman answer is fine but I think that we can improve it by inlining the assertion in the Answer object itself. It avoids creating unnecessary copy objects and additional local variable(s).
Besides in cases of we need to copy the state of custom objects (by performing a deep copy of it), this way is much simpler. Indeed, it doesn't require any custom code or library to perform the copies as the assertion is done on the fly.
In Java 8, it would give :
import static org.mockito.Mockito.*;
when(sender.send(any())).thenAnswer(invocation -> {
List<LogMessage> listAtMockTime = invocation.getArguments()[0];
Assert.assertEquals(5, listAtMockTime.getSize());
});
bufferedSender.flushBuffer();
Note that InvocationOnMock.getArgument(int index) returns an unbounded wildcard (?). So no cast is required from the caller as the returned type is defined by the target : here the declared variable for which one we assign the result.
You would have the same issue than with ArgumenCaptor as the verify() method checks the invocation with the state of the object after the execution. No capture is performed to keep only the state at the invocation time.
So with a mutable object I think that a better way would be to not use Mockito and instead create a stub of the Sender class where you capture the actual size of the collection as send() is invoked.
Here is a sample stub class (minimal example that you could of course enrich/adapt) :
class SenderStub extends Sender {
private int bufferSize;
private boolean isSendInvoked;
public int getBufferSize() {
return bufferSize;
}
public boolean isSendInvoked(){
return isSendInvoked;
}
#Override
public void send(List<LogMessage> buffer ) {
this.isSendInvoked = true;
this.bufferSize = buffer.size();
}
}
Now you have a way to check whether the Sender was invoked and the size (or even more) of that.
And so put aside Mockito to create this mock and verify its behavior :
SenderStub sender = new SenderStub();
MyClassToTest myClass = new MyClassToTest(sender);
// action
myClass.flushBuffer();
// assertion
Assert.assertTrue(sender.isInvoked());
Assert.assertEquals(5, sender.getBufferSize());
I am using Mockito for service later unit testing. I am confused when to use doAnswer vs thenReturn.
Can anyone help me in detail? So far, I have tried it with thenReturn.
You should use thenReturn or doReturn when you know the return value at the time you mock a method call. This defined value is returned when you invoke the mocked method.
thenReturn(T value) Sets a return value to be returned when the method is called.
#Test
public void test_return() throws Exception {
Dummy dummy = mock(Dummy.class);
int returnValue = 5;
// choose your preferred way
when(dummy.stringLength("dummy")).thenReturn(returnValue);
doReturn(returnValue).when(dummy).stringLength("dummy");
}
Answer is used when you need to do additional actions when a mocked method is invoked, e.g. when you need to compute the return value based on the parameters of this method call.
Use doAnswer() when you want to stub a void method with generic Answer.
Answer specifies an action that is executed and a return value that is returned when you interact with the mock.
#Test
public void test_answer() throws Exception {
Dummy dummy = mock(Dummy.class);
Answer<Integer> answer = new Answer<Integer>() {
public Integer answer(InvocationOnMock invocation) throws Throwable {
String string = invocation.getArgumentAt(0, String.class);
return string.length() * 2;
}
};
// choose your preferred way
when(dummy.stringLength("dummy")).thenAnswer(answer);
doAnswer(answer).when(dummy).stringLength("dummy");
}
doAnswer and thenReturn do the same thing if:
You are using Mock, not Spy
The method you're stubbing is returning a value, not a void method.
Let's mock this BookService
public interface BookService {
String getAuthor();
void queryBookTitle(BookServiceCallback callback);
}
You can stub getAuthor() using doAnswer and thenReturn.
BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return "Joshua";
}
}).when(service).getAuthor();
Note that when using doAnswer, you can't pass a method on when.
// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());
So, when would you use doAnswer instead of thenReturn? I can think of two use cases:
When you want to "stub" void method.
Using doAnswer you can do some additionals actions upon method invocation. For example, trigger a callback on queryBookTitle.
BookServiceCallback callback = new BookServiceCallback() {
#Override
public void onSuccess(String bookTitle) {
assertEquals("Effective Java", bookTitle);
}
};
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
callback.onSuccess("Effective Java");
// return null because queryBookTitle is void
return null;
}
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
When you are using Spy instead of Mock
When using when-thenReturn on Spy Mockito will call real method and then stub your answer. This can cause a problem if you don't want to call real method, like in this sample:
List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));
Using doAnswer we can stub it safely.
List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));
Actually, if you don't want to do additional actions upon method invocation, you can just use doReturn.
List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
The simplest answer is:
If you need a fixed return value on method call then we should use thenReturn(…)
If you need to perform some operation or the value need to be computed at run time then we should use thenAnswer(…)
To check the number of interactions with a mock where the parameter in the method call is of a certain type, one can do
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(isA(FirstClass.class));
This will pass thanks to the call to isA since someMethod was called twice but only once with argument FirstClass
However, this pattern seems to not be possible when using an ArgumentCaptor, even if the Captor was created for the particular argument FirstClass
this doesn't work
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
verify(mock, times(1)).someMethod(captor.capture());
it says the mock was called more than once.
Is there any way to accomplish this verification while capturing the argument for further checking?
I recommend using Mockito's Hamcrest integration to write a good, clean matcher for it. That allows you to combine the verification with detailed checking of the passed argument:
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
verify(mock, times(1)).someMethod(argThat(personNamed("Bob")));
Matcher<Person> personNamed(final String name) {
return new TypeSafeMatcher<Person>() {
public boolean matchesSafely(Person item) {
return name.equals(item.getName());
}
public void describeTo(Description description) {
description.appendText("a Person named " + name);
}
};
}
Matchers generally lead to more readable tests and more useful test failure messages. They also tend to be very reusable, and you'll find yourself building up a library of them tailored for testing your project. Finally, you can also use them for normal test assertions using JUnit's Assert.assertThat(), so you get double use out of them.
Quoting the docs:
Note that an ArgumentCaptordon't do any type checks, it is only
there to avoid casting in your code. This might however change (type
checks could be added) in a future major release.
I wouldn't use an ArgumentCaptor for this. This class captures (literally) everything, despite what class was provided as it's .forClass argument.
To achieve what you want I suggest intercept the argument using Mockito's Answer interface:
private FirstClass lastArgument;
#Test
public void captureFirstClass() throws Exception {
doAnswer(captureLastArgument()).when(mock).someMethod(anInstanceOfFirstClass());
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(anInstanceOfFirstClass());
//write your desired matchers against lastArgument object
}
private Answer<FirstClass> captureLastArgument() {
return new Answer<FirstClass>() {
#Override
public FirstClass answer(InvocationOnMock invocation) throws Throwable {
TestClass.this.lastArgument = (FirstClass) invocation.getArguments()[0];
return null;
}
};
}
private static Object anInstanceOfFirstClass(){
return Mockito.argThat(isA(FirstClass.class));
}
You can use the the captor for the sake of capturing, then verify the number of invocations with each argument type separately.
// given
ArgumentCaptor<AA> captor = ArgumentCaptor.forClass(AA.class);
CC cc = new CC();
// when
cut.someMethod(new AA());
cut.someMethod(new BB());
cut.someMethod(new BB());
cut.someMethod(cc);
// then
Mockito.verify(collaborator, atLeastOnce()).someMethod(captor.capture());
Mockito.verify(collaborator, times(1)).someMethod(isA(AA.class));
Mockito.verify(collaborator, times(2)).someMethod(isA(BB.class));
Mockito.verify(collaborator, times(1)).someMethod(isA(CC.class));
assertEquals(cc, captor.getValue());
Apparently the generic type of the captor reference doesn't affect anything at runtime.
I also encountered this problem today. I thought I could simply do something like
verify(mock).someMethod(and(isA(FirstClass.class), captor.capture()));
but I couldn't get it to work. I ended up with this solution:
#Test
public void Test() throws Exception {
final ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(eventBus, atLeastOnce()).post(captor.capture());
final List<FirstClass> capturedValues = typeCheckedValues(captor.getAllValues(), FirstClass.class);
assertThat(capturedValues.size(), is(1));
final FirstClass capturedValue = capturedValues.get(0);
// Do assertions on capturedValue
}
private static <T> List<T> typeCheckedValues(List<T> values, Class<T> clazz) {
final List<T> typeCheckedValues = new ArrayList<>();
for (final T value : values) {
if (clazz.isInstance(value)) {
typeCheckedValues.add(value);
}
}
return typeCheckedValues;
}
Note: if only one class needs to be captured in this way typeCheckedValues can be simplified into:
private static List<FirstClass> typeCheckedValues(List<FirstClass> values) {
final List<FirstClass> typeCheckedValues = new ArrayList<>();
for (final Object value : values) {
if (value instanceof FirstClass) {
typeCheckedValues.add((FirstClass) value);
}
}
return typeCheckedValues;
}