I am trying to use mockito to mock a method. However the class I am injecting mocks with calls the method twice while sending in two different objects of the same type, but depending the values in the object determine the output of the method.
So, for example, If I am trying to mock
public ArrayList<example> attemptToMock(testObject testing)
Lets sat type testObject
has a string value in it.
So if the string value in testObject is "OK" then attemptToMock
should output an array of two objects in it. If testObject
string value is "NO" then the Array list sent out only has one Object.
How to I write a test to handle a call so that a class can call attemptToMock
twice, within the same method, and I can mock out its output it so depending on the values within testObject
. I can mock it to send out different arrays.
A few options:
Override equals
and hashCode
on your object (TestObject). This is only feasible if all of the values on your object are predictable, and may be more work than other solutions, but if you need to write equals
and hashCode
anyway (for Map and Set behavior, for example) this is a reasonable solution.
// Mockito compares with objects' equals(Object) methods by default.
when(collaborator.attemptToMock(object1)).thenReturn(array1);
when(collaborator.attemptToMock(object2)).thenReturn(array2);
Write a Hamcrest matcher and use that to match the arrays. This acts as a compact analogue to equals
for a specific case, and is especially handy if you need to change behavior based on the same value in many tests.
public class IsATestObjectWithValue extends TypeSafeMatcher<TestObject> {
private final String expectedValue;
public IsATestObjectWithValue(String expectedValue) {
super(TestObject.class);
this.expectedValue = expectedValue;
}
@Override public void matchesSafely(TestObject object) {
// object will never be null, but object.value might.
return expectedValue.equals(object.value);
}
}
Now you can write an equivalent match as above:
when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("OK")))
.thenReturn(array1);
when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("NO")))
.thenReturn(array2);
Use an Answer, as wdf described. Anonymous inner Answers are common and pretty concise, and you get access to all of the arguments. This is especially good for one-off solutions, or if you want to explicitly and immediately fail the test if an invalid value (testObject.value) is passed in.
As a last resort, if the order of the calls is predictable, you can return multiple values in sequence.
when(collaborator.attemptToMock(any(TestObject.class)))
.thenReturn(array1).thenReturn(array2);
when(collaborator.attemptToMock(any(TestObject.class)))
.thenReturn(array1, array2); // equivalent
Either of the above lines will return array1
for the first call and array2
for the second call and all calls after it, regardless of the parameter. This solution will be much more brittle than your original question asks for--it'll fail if the call order changes, or if either of the calls is edited out or repeated--but is sometimes the most compact solution if the test is very temporary or if the order is absolutely fixed.