1. Overview
In this tutorial, we’ll see how to avoid ambiguous method calls in the specific context of the Mockito framework.
In Java, method overloading allows a class to have multiple methods with the same name but different parameters. An ambiguous method call happens when the compiler can’t determine the concrete method to invoke based on the provided arguments.
2. Introducing Mockito’s ArgumentMatchers
Mockito is a mocking framework for unit testing Java applications. The latest version of the library can be found in the Maven Central Repository. Let’s add the dependency to our pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
ArgumentMatchers are part of the Mockito framework: thanks to them, we can specify the mocked method’s behavior when the arguments match given conditions.
3. Overloaded Methods Definition
First, let’s define a method that takes an Integer as a parameter and always return 1 as a result:
Integer myMethod(Integer i) {
return 1;
}
For the sake of our demonstration, we’ll want our overloaded method to use a custom type. Thus, let’s define this dummy class:
class MyOwnType {}
We can now add an overloaded myMethod() that accepts a MyOwnType object as an argument and always return baeldung as a result:
String myMethod(MyOwnType myOwnType) {
return "baeldung";
}
Intuitively, if we pass a null argument to myMethod(), the compiler won’t know which version it should use. Moreover, we can note that the method’s return type has no impact on this problem.
4. Ambiguous Call with isNull()
Let’s naively try to mock a call to myMethod() with a null parameter by using the base isNull() ArgumentMatcher:
@Test
void givenMockedMyMethod_whenMyMethod_ThenMockedResult(@Mock MyClass myClass) {
when(myClass.myMethod(isNull())).thenReturn(1);
}
Given we called the class where myMethod() is defined MyClass, we nicely injected a mocked MyClass object via the test’s method parameters. We can also note that we didn’t add any assertion to the test yet. Let’s run this code:
java.lang.Error: Unresolved compilation problem:
The method myMethod(Integer) is ambiguous for the type MyClass
As we can see, the compiler can’t decide which version of myMethod() to use, thus throwing an error. Let’s underline that the compiler’s decision is only based on the method argument. Since we wrote thenReturn(1) in our instruction, as readers, we could guess that the intention is to use the version of myMethod() that returns an Integer. However, the compiler won’t use this part of the instruction in its decision process.
To solve this problem, we need to use the overloaded isNull() ArgumentMatcher that takes a class as an argument instead. For instance, to tell the compiler it should use the version that takes an Integer as a parameter, we can write:
@Test
void givenMockedMyMethod_whenMyMethod_ThenMockedResult(@Mock MyClass myClass) {
when(myClass.myMethod(isNull(Integer.class))).thenReturn(1);
assertEquals(1, myClass.myMethod((Integer) null));
}
We added an assertion to complete the test, and it now runs successfully. Similarly, we can modify our test to use the other version of the method:
@Test
void givenCorrectlyMockedNullMatcher_whenMyMethod_ThenMockedResult(@Mock MyClass myClass) {
when(myClass.myMethod(isNull(MyOwnType.class))).thenReturn("baeldung");
assertEquals("baeldung", myClass.myMethod((MyOwnType) null));
}
Lastly, let’s notice that we needed to give the type of null in the call to myMethod() in the assertion as well. Otherwise, this would throw for the same reason!
5. Ambiguous Call With any()
In the same way, we can try to mock a myMethod() call that accepts any argument by using the any() ArgumentMatcher:
@Test
void givenMockedMyMethod_whenMyMethod_ThenMockedResult(@Mock MyClass myClass) {
when(myClass.myMethod(any())).thenReturn(1);
}
Running this code once again results in an ambiguous method call error. All the remarks we made in the previous case are still valid here. In particular, the compiler fails before even looking at the thenReturn() method’s argument.
The solution is also similar: we need to use a version of the any() ArgumentMatcher that clearly states what is the type of the expected argument:
@Test
void givenMockedMyMethod_whenMyMethod_ThenMockedResult(@Mock MyClass myClass) {
when(myClass.myMethod(anyInt())).thenReturn(1);
assertEquals(1, myClass.myMethod(2));
}
Most base Java types already have a Mockito method defined for this purpose. In our case, the anyInt() method will accept any Integer argument. On the other hand, the other version of myMethod() accepts an argument of our custom MyOwnType type. Thus, we’ll need to use the overloaded version of the any() ArgumentMatcher that takes the object’s type as an argument:
@Test
void givenCorrectlyMockedNullMatcher_whenMyMethod_ThenMockedResult(@Mock MyClass myClass) {
when(myClass.myMethod(any(MyOwnType.class))).thenReturn("baeldung");
assertEquals("baeldung", myClass.myMethod((MyOwnType) null));
}
Our tests are now working fine: we successfully eliminated the ambiguous method call error!
6. Conclusion
In this article, we understood why we can face an ambiguous method call error with the Mockito framework. Additionally, we showcased the solution to the problem. In real life, this kind of problem is most likely to arise when we’ve got overloaded methods with tons of arguments, and we decide to use the less constraining isNull() or any() ArgumentMatcher because the value of some of the arguments isn’t relevant to our test. In simple cases, most modern IDEs can point out the problem before we even need to run the test.
As always, the code is available over on GitHub.