Keywords: Mockito | Unit Testing | Java 8 Type Inference
Abstract: This technical article explores the proper application of Mockito's generic any() method for argument verification in unit tests, focusing on type inference improvements in Java 8 and beyond. It compares any() with anyObject() and discusses type-safe approaches for arrays and primitive types, including practical code examples and explanations of compiler behavior and type erasure implications.
Introduction to Argument Matchers in Mockito
Mockito provides several argument matchers to verify method invocations in unit tests. The any() method, introduced as a generic alternative to anyObject(), offers improved type safety and readability. This article examines its correct usage, particularly with array parameters and primitive types, leveraging Java 8's enhanced type inference.
Replacing anyObject() with any() for Array Parameters
Consider an interface with a method accepting an array of Foo objects:
public interface IBar {
void doStuff(Foo[] arr);
}
To verify that doStuff() is called without validating the specific arguments, you can use any() instead of the deprecated anyObject(). The preferred approach is:
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
IBar bar = mock(IBar.class);
// ... test setup and method invocation
verify(bar).doStuff(any(Foo[].class));
This method is type-safe, as it explicitly specifies the expected argument type, reducing the risk of runtime errors.
Leveraging Java 8 Type Inference with any()
With Java 8, you can use the argument-less any() method, where the compiler infers the type parameter based on the target type—the parameter type of the method being verified. For example:
verify(bar).doStuff(any());
This works because Java 8 uses the target type (here, Foo[]) to infer the return type of any(), which becomes Foo[]. This improvement, initially added for lambda expressions, enhances type inference generally, making code more concise.
Handling Primitive Types with any()
While any() works well with reference types, it is unsuitable for primitive types due to type erasure and unboxing issues. For instance, with an interface method expecting an int:
public interface IBar {
void doPrimitiveStuff(int i);
}
Using any() compiles but causes a NullPointerException:
verify(bar).doPrimitiveStuff(any()); // Throws NullPointerException
The compiler infers Integer as the return type, but Mockito returns null (the default for reference types). The runtime attempts to unbox null to int, leading to the exception. Instead, use specific matchers like anyInt():
verify(bar).doPrimitiveStuff(anyInt()); // Correct approach
Alternative Approaches and Best Practices
For scenarios requiring argument inspection, ArgumentCaptor is useful:
ArgumentCaptor<Foo[]> captor = ArgumentCaptor.forClass(Foo[].class);
verify(bar).doStuff(captor.capture());
Foo[] capturedArray = captor.getValue();
// Perform assertions on capturedArray
This allows detailed verification of argument contents but is more verbose than any(). When using any(), prefer the explicit form (any(Foo[].class)) for clarity and type safety, especially in complex codebases.
Conclusion
Mockito's any() method provides a robust way to verify method calls without argument validation. By understanding Java 8 type inference and limitations with primitive types, developers can write cleaner, more reliable unit tests. Adopting these practices enhances test maintainability and reduces errors in mocking scenarios.