'matching List in any order when mocking method behavior with Mockito
I have a test using Mockito that has a very strange behavior : it works in debug but fails when running normally. After some investigation, I realized it's because I am mocking methods behavior, passing a list of elements to match. But for some reason, order in the list is not always the same so it doesn't match and what I expect my mock to return is not returned, because the 2 lists are not "equals"
when(mockStatusCalculatorService.calculateStatus(Arrays.asList(IN_PROGRESS, ABANDONNED,EXPIRED))).thenReturn(ConsolidatedStatus.EXPIRED);
In my case, order of elements to match doesn't matter. So how can I specify this when configuring my mock ?
Solution 1:[1]
If you have Mockito prior to version 2.1.0:
Use the Hamcrest containsInAnyOrder
matcher.
when(myMock.myMethod(argThat(containsInAnyOrder(IN_PROGRESS, ABANDONED, EXPIRED))))
.thenReturn(myValue);
Thank you to @kolobok for pointing out that as from Mockito 2.1.0 (which came out after I wrote this answer), this no longer works.
So for version 2.1.0 and above:
add a dependency on Hamcrest, and use the MockitoHamcrest.argThat
instead of Mockito.argThat
More detail on the breaking change with Mockito 2.1.0 is at https://www.javadoc.io/doc/org.mockito/mockito-core/2.1.0/org/mockito/ArgumentMatcher.html
Solution 2:[2]
Adding an answer for newer versions of Mockito and Java 8
when(
mock.method(argThat(t -> t.containsAll(Arrays.asList(IN_PROGRESS, ABANDONED, EXPIRED))))
).thenReturn(myValue);
Solution 3:[3]
it's actually quite simple. we need a custom matcher :
import org.apache.commons.collections.CollectionUtils;
import org.mockito.ArgumentMatcher;
import java.util.List;
import static org.mockito.Matchers.argThat;
public class InAnyOrderListMatcher extends ArgumentMatcher<List> {
private final List expected;
public InAnyOrderListMatcher(List expected){
this.expected=expected;
}
@Override
public boolean matches(Object actual) {
if(actual instanceof List){
List actualList=(List)actual;
return CollectionUtils.isEqualCollection(expected,actualList);
}
return false;
}
public static List inAnyOrderListMatcherEq(List expected) {
return argThat(new InAnyOrderListMatcher(expected));
}
}
And then call it in the test :
when(mockStatusCalculatorService.calculateStatus( inAnyOrderListMatcherEq(Arrays.asList(IN_PROGRESS, ABANDONNED,EXPIRED)))).thenReturn(ConsolidatedStatus.EXPIRED);
Solution 4:[4]
If order does not matter, change your StatusCalculator service to accept a Set instead of a Collection. Then equals will return true regardless of order.
Better to fix your API than hack around it in your unit tests.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | shinjw |
Solution 3 | Vincent F |
Solution 4 |