Francois
Francois

Reputation: 626

ArgumentMatchers.any() versus the other matchers in Mockito 2

With Mockito 2, should ArgumentMarchers.any() be used instead of more specific matchers like ArgumentMatchers.anyString() or ArgumentMatchers.anyList() for example? Should specific matchers be used to make the code more readable?

From experience, when using native objets (int, long, double, boolean), specific matchers anyInt(), anyLong(), anyDouble() or anyBoolean() is preferred. But what about the other matchers? Any ideas? Thanks.

Upvotes: 8

Views: 21147

Answers (1)

Jeff Bowman
Jeff Bowman

Reputation: 95654

In short, you could use either one in most cases (especially in Java 8 or newer). any() is often more idiomatic to non-brittle Mockito tests, though there are some pragmatic reasons and some correctness reasons to use anyString or any(String.class). Beware: the full rationale delves deeply into Mockito matcher internals, Java type parameter inference, and a whole lot of hysterical raisins.


For primitives like anyFloat(), there is a very practical reason to prefer anyFloat() etc instead of any(): The latter will give you a NullPointerException. For Mockito syntax like when(floatAcceptor.acceptFloat(any())).then(/*...*/), Mockito will actually call your mocked floatAcceptor.acceptFloat(float) method, and calls to any() necessarily return null because Mockito matchers have to return a dummy value and Java doesn't tell Mockito enough for it to know to return a float-compatible value. Irrespective of Mockito, Java will try to unbox a null into a float and fail. (anyFloat() and any(Float.class) tell Mockito to expect a float, so they correctly return 0.0f.) If you know a value is a primitive, boxed or unboxed, it's safer to call the appropriate method. That reason goes away for List and String, which are objects through and through; Java will happily pass a null received from any.

Historically, any behaved identically to any(Class), anyString(), and anyList(Class); this was an important convenience, because Java 7 couldn't infer type arguments from parameters, so the alternative was (Foo) any() or ArgumentMatchers.<Foo>any(). Compared to those, any(Foo.class) is more readable. It gets even worse for Lists, because type literals don't support other types, so any(List.class) won't even work for List<Bar>; you'd need (List<Bar>) any(), or ArgumentMatchers.<List<Bar>>any(), but with anyList you can just write anyList(Bar.class) and be on your way. In all cases, the argument was entirely ignored, and to check types you'd need to use isA(Class) to do a reflective, null-rejecting, instanceof-style check.

However, two improvements happened that change this: Java 8 happily infers type arguments through parameters, so any() is much more usable, and Mockito corrected its syntax to appear more like English. In English, "any car" wouldn't likely include a bicycle or an empty parking spot, but in Mockito 1.x any(Car.class) would happily match invocations with a null or a Bicycle instance. Thus, in Mockito 2.x, any(String) and anyString() will only accept non-null Strings, as documented in GitHub issue #185. Same with all other any(Class) calls.

To top it all off, Mockito prefers flexible tests rather than brittle ones, so you are much more likely to see any() for an irrelevant Foo parameter than isA(Foo.class) or eq(new Foo()). Irrelevant parameters are ignored by convention, if changing them is unlikely to affect the behavior being tested. That said, anyString() and anyList() can be useful for readability when you know the argument isn't null, and can also help you keep track of long argument lists by failing to compile if your list changes in an incompatible way to the method you're calling.

So in summary: Use primitive methods for primitives, use any() as much as possible, but switch to any(Class), any(List), or any(String) if you want to check non-null types are correct, if you want to improve readability, or you need to maintain long frequently-changing argument lists.

You can read more on the semantics of any(), any(Class), and isA on this GitHub issues answer.

Upvotes: 15

Related Questions