Working of Mockito's verify and argument captor - java

I'm trying to test the following method
public void saveToMultipleSources(MyObject myObject)
{
if (myObject.isOfSomeType()) {
firstDAO.saveObject(myObject);
myObject.setContent(null);
}
secondDAO.saveObject(myObject);
}
The test I wrote was
#Test
public void testSaveFeature()
{
//initialize all objects
obj.saveToMultipleSources(myObject); //myObject has a valid content.
Mockito.verify(firstDAO).saveObject(myObject);
myObject.setContent(null);
Mockito.verify(secondDAO).saveObject(myObject);
}
But on running, I get the error that expected and actual arguments differ for the verify statement of firstDAO. The expected was an object with valid content but actual arguments invoked are with Content set as null. I tried the exact same thing with ArgumentCaptor as well, but got the same results.
Can someone explain why does Mockito behave this way? I tried logging the entire object and can see valid Content being set just before I call the firstDAO.
Also how do I go about testing this?

//initialize all objects
obj.saveToMultipleSources(myObject); //myObject has a valid content.
Mockito.verify(firstDAO).saveObject(myObject);
The problem is that setting the objects content to null is a side effect of your method. In consequence Mockito compares the recorded parameter (having valid content) with the object modified by your method (having content already set to null).
In that case, how can I test this? – Abhilash Panigrahi
In the test Make myObject a Mockito.spy() and prevent the execution of setContent():
#Test
public void testSaveFeature()
{
//initialize all objects
MyObject spyOfMyObject = Mockito.spy(myObject);
doNothing().when(spyOfMyObject).setContent(null); // special null matcher may be needed...
obj.saveToMultipleSources(spyOfMyObject);
Mockito.verify(firstDAO).saveObject(spyOfMyObject);
Mockito.verify(spyOfMyObject).setContent(null);
Mockito.verify(secondDAO).saveObject(spyOfMyObject);
}
But you most likely want to be sure that myObject.setContent(null);
is called before econdDAO.saveObject(myObject);`...
#Test
public void testSaveFeature()
{
//initialize all objects
MyObject spyOfMyObject = Mockito.spy(myObject);
doNothing().when(spyOfMyObject).setContent(null);
obj.saveToMultipleSources(spyOfMyObject);
Mockito.verify(firstDAO).saveObject(spyOfMyObject);
InOrder inOrder = Mockito.inOrder(spyOfMyObject,secondDAO);
inOrder.verify(spyOfMyObject).setContent(null);
inOrder..verify(secondDAO).saveObject(spyOfMyObject);
}

Related

How to mock local object property which is been set in another method

I have similar to below code in my application.
public String someMethod(final Object obj) {
final ValidationResponse validationResponse = new ValidationResponse();
String responseMessage = validatorService.validate(obj, validationResponse);
if(validationResponse.isValid()) {
//Positive flow
}
else {
//Negative flow
}
return responseMessage;
}
I am writing JUnit test cases to generate Mutation's report. As the object validationResponse is used which is a local object created in the flow. Mockito is unable to get & return desired value for isValid. Due to this I am unable to cover the test cases for the Positive flow.
How this could be achieved? Any lead is very much appreciated.
I got the solution to this problem from one of my teammate. It is as below
Mockito.doNothing(invocation ->
{
ValidationResponse validationResponse = invocation.getArgument(0);
validationResponse.setValida(true);//Set value true or false based on the mock test case scenario.
return null;
})
.when(validatorService)
.validate(obj, validationResponse));
This mocks the value of the validation response property.
The flow of this method is determined by this line
String response = validator1.validate(obj, validationResponse);
If you want to excercise both branches of the following if statment you need to control the modification that validator1 makes to the validationResponse object.
This can be done in two ways.
If your test can inject the instance of validator1 (eg via the code under tests constructor) then you can fake it.
This is probably easiest to do with a hand coded implementation rather than a mocking framework.
eg
class AlwaysInvalid implements WhateverTheTypeOfValidator1Is {
void validate(Object unused, ValidationResponse response) {
response.setInvalidOrWhatever();
}
}
Alternatively you can use a real collaborator, in which case your tests needs to ensure that objects are passed into someMethod that result in both valid and invalid responses.

Testing Methods that create objects inside the method with Junit and Mockito

Hello I am fairly new to unit testing with Junit as well as Mockito. I think I have a fairly reasonable understanding of the principles but I can't seem to find any explanations of what I am specifically trying to test online.
I want to test a method, that calls several other methods (void and non-void), which also instantiates objects in the method body. I unfortunately cannot share the code as it is not mine, but here is a general format:
class classToTest {
private final field_1;
public void methodToTest( string id, List object_1, List object_2) {
try {
Map<SomeObject_1, SomeObject_2> sampleMap = new HashMap<>();
method_1(object_1, object_2); //void function modifies object_2
field_1.method_2(id, object_2);
Map<SomObeject_1, List<object>> groupedList = groupList(object_2)
//Then some stuff is added to the sampleMap
}
//catch would be here
}
At the moment I only care about testing method_1, and I cannot test directly as it is a private method so I must go through this parent method call. I wish I could change the code but I have been asked to keep it the same and to test in this manner with Mockito and Junit.
I know I need to Mock an object of the class to Test as well as its parameter:
private classToTest classToTestObject;
#Mock private field_1 f1;
#Before
public void setup() {
MockitoAnnotations.init.Mocks(this);
classToTestObject = mock(classToTest.class, CALLS_REAL_METHODS);
}
but I don't know where to start my actual test, as in how I can essentially just execute that one method call and ignore all the rest. I can't just not ignore the other objects and method calls either as the main method will throw exceptions if they are not handled correctly.
Any help and guidance is much appreciated, sorry that I could not share the code. Thank You!
At the moment I only care about testing method_1, and I cannot test directly as it is a private method so I must go through this parent method call.
Per your comment, and the note in your code:
method_1(object_1, object_2); //void function modifies object_2
You would set up a test that allows you to verify the expected final state of object_2. You would do this with a real instance of the class, not a mock.
#Test
public void method1Test() {
// Assemble - your preconditions
ClassToTest subject = new ClassToTest();
List<SomeType> object_1 = new ArrayList();
List<SomeOtherType> object_2 = new ArrayList();
// Populate object_1 and object_2 with data to use as input
// that won't throw exceptions. Call any methods on subject that put
// it in the desired state
// Act - call the method that calls the method under test
subject.methodToTest("some id that makes the method run correctly", object_1, object_2);
// Assert - one or more assertions against the expected final state of object_2
assertThat(object_2).matchesYourExpectations();
}

How to capture same object multiple times in Mockito argument captor

I was writing a Junit to test a scenario where an object changes its variables and saves it into the database 2 times. The argumentCaptor is invoked in save operation. The getAllValues() is returning two records. But both values are referenced to the same last capture record.
ImplimentationClass.java
...
myObj.setVariable(oneValue);
saveMyObj(myObj);
myObj.setVariable(otherValue);
saveMyObj(myObj);
...
saveMyObj(MyObject myObj){
repository.save(myObj);
}
ImplimentationClassTest.java
private ImplimentationClass underTest ;
#Mock
private Repository mockRepository;
#Before
public void setup(){
initMocks(this);
underTest = new ImplimentationClassTest();
}
#Test
public void test(){
ArgumentCaptor<MyObject> captor = ArgumentCaptor.forClass(MyObject.class);
MyObject obj = new MyObject(value);
underTest.implementedMethod(obj);
verify(mockRepository, times(2)).save(captor.capture());
assertEquals(oneValue, captor.getAllValues().get(0).getVariable()); //failing here -getting otherValue
assertEquals(otherValue, captor.getAllValues().get(1).getVariable());
}
Any idea how to capture same object multiple times?
The problem from your test originates from this piece of code.
myObj.setVariable(oneValue);
saveMyObj(myObj);
myObj.setVariable(otherValue);
saveMyObj(myObj);
Once you change the variable inside of myObj you change it for all references. Note that the ArgumentCaptor does not make a deep copy of myObj.
So you will end up with two references to myObj, which only has the latest state.
To avoid this you might want to pass a different instance to the second call.
Another alternative might be to use doAnswer instead and check the correctness of the paramter inside that method.
Check this answer for an example.

Mocking ColumnDefinitions.Definition does return mock, but is behaves like null in the tested code

I have following code preparing mocks to test my service using Cassandra (I need to mock com.datastax.driver.core.ColumnDefinitions.Definition) :
#RunWith(PowerMockRunner.class)
public class TestMyClass{
private MyClass target;
#Before
public void setUp() throws Exception {
ColumnDefinitions mockColumnDefinitions=Mockito.mock(ColumnDefinitions.class);
Mockito.when(mockRow.getColumnDefinitions()).thenReturn(mockColumnDefinitions);
target= new MyClass();
Definition mockDef = Mockito.mock(Definition.class);
List<Definition> defList = new ArrayList<Definition>();
defList.add(mockDef);
Iterator mockIterator = Mockito.mock(Iterator.class);
Mockito.when(mockColumnDefinitions.iterator()).thenReturn(mockIterator);
Mockito.when(mockIterator.hasNext()).thenReturn(true, false);
Mockito.when(mockIterator.next()).thenReturn(mockDef);
Mockito.when(mockDef.getName()).thenReturn(NAME);
}
#Test
public void testMyMethod() throws Exception {
target.MyMethod();
}
}
Test execution goes fine this place, and I have this type of code in different places, so it should work.
Inside the service I am testing I have following code:
ColumnDefinitions colDef = row.getColumnDefinitions();
Iterator<Definition> defIterator = colDef.iterator();
while (defIterator.hasNext()) {
Definition def = defIterator.next();
String columnName = def.getName();
}
When I debug this code, I see, that both colDef and defIterator are mocked successfully. I see something like that in debug variables area:
Mock for Iterator, hashCode: 430126690
But after defIterator.next() invocation I see that though def is an object and not null, it doesn't show hashcode like for Iterator, instead I see this:
com.sun.jdi.InvocationException occurred invoking method.
And after invoking this string:
String columnName = def.getName();
I immediately get NullPointerException like if def is null.
What am I doing wrong?
Thanks.
EDIT 1 ________________________________________________________________________
I also tried to use PowerMockito with the same methods instead, the result is the same.
EDIT 2 ________________________________________________________________________
I added the whole test method code.
It is been a while since this question was created. I have faced this same problem few days ago and I have solved it in the following manner (I hope my proposed solution helps someone in the future):
First of all, I want to clarify that ColumnDefinition.Definition class is a public static nested class that has four private final fields and only has one constructor: Definition (String keyspace, String table, String name and DataType type) (for more details please refer to the ColumnDefinitions.Definition javadoc and ColumnDefinitions source code). Therefore this nested class could not be mocked by Mockito nor Powermock because of its final fields.
SOLUTION:
I had to create a real object, not a mocked one of the class ColumnDefinition.Definition using reflection, so you can initialise the mockDef object as follows:
Constructor<Definition> constructor = (Constructor<Definition>) Definition.class.getDeclaredConstructors()[0]; // as Definition only has one constructor, 0 will be passed as index
constructor.setAccessible(true);
Definition mockDef = constructor.newInstance("keyspace", "table", "name", null);
replacing this line of code in your snippet:
Definition mockDef = Mockito.mock(Definition.class);
Then the NullPointerException will never be thrown again when executing this line of code:
String columnName = def.getName();

Matching an array of Objects using Mockito

I'm trying to set up a mock for a method that takes an array of Request objects:
client.batchCall(Request[])
I've tried these two variations:
when(clientMock.batchCall(any(Request[].class))).thenReturn(result);
...
verify(clientMock).batchCall(any(Request[].class));
and
when(clientMock.batchCall((Request[])anyObject())).thenReturn(result);
...
verify(clientMock).batchCall((Request[])anyObject());
But I can tell the mocks aren't being invoked.
They both result in the following error:
Argument(s) are different! Wanted:
clientMock.batchCall(
<any>
);
-> at com.my.pkg.MyUnitTest.call_test(MyUnitTest.java:95)
Actual invocation has different arguments:
clientMock.batchCall(
{Request id:123},
{Request id:456}
);
Why does the matcher not match the array? Is there a special matcher I need to use to match an array of objects? The closest thing I can find is AdditionalMatches.aryEq(), but that requires that I specify the exact contents of the array, which I'd rather not do.
So I quickly put something together to see if I could find your issue, and can't below is my sample code using the any(Class) matcher and it worked. So there is something we are not seeing.
Test case
#RunWith(MockitoJUnitRunner.class)
public class ClientTest
{
#Test
public void test()
{
Client client = Mockito.mock(Client.class);
Mockito.when(client.batchCall(Mockito.any(Request[].class))).thenReturn("");
Request[] requests = {
new Request(), new Request()};
Assert.assertEquals("", client.batchCall(requests));
Mockito.verify(client, Mockito.times(1)).batchCall(Mockito.any(Request[].class));
}
}
client class
public class Client
{
public String batchCall(Request[] args)
{
return "";
}
}
Request Class
public class Request
{
}
Necroposting, but check whether the method you're calling is declared as batchCall(Request[] requests) or batchCall(Request... requests).
If it's the latter, try when(clientMock.batchCall(Mockito.anyVararg())).
I had the same issue and the reason for me was, that the elements in the arrays had different orders.

Categories