Search code examples
javamockingmockitopowermockmatcher

Mockito returnsFirstArg() doesn't work with generic first arg


EDIT: I've finally created an issue on mockito github project.

I'm trying to mock the typed method getNameElement of Interface RoomGeneralService to return the first arg, using Mockito AdditionalAnswers.returnsFirstArg functionality:

Interface to mock:

interface PrimaryKeyElement<T> {
   public String getNameElement(T primaryKey);
}

interface RoomGeneralService extends PrimaryKeyElement<String> {
   // ...
}

My test (note the imports)

import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

@RunWith(PowerMockRunner.class)
public class SampleTest {

   @Mock
   RoomGeneralService roomGeneralService;

   @Test
   public void testFoo() throws Exception {
      when(roomGeneralService.getNameElement(anyString())).thenAnswer(returnsFirstArg());
      //...
   }
}

Also I've tried with other combinations, but without success so far:

when(roomGeneralService.getNameElement(Matchers.<String>any())).thenAnswer(returnsFirstArg());
doAnswer(returnsFirstArg()).when(roomGeneralService.getNameElement(anyString()));
doReturn(returnsFirstArg()).when(roomGeneralService.getNameElement(anyString()));

Error received:

The reason for this error can be : 1. The wanted argument position is incorrect. 2. The answer is used on the wrong interaction.

Position of the wanted argument is 0 and the possible argument indexes for this method are : [0] Object

Workaround:

I know I can create my own answer, and in fact it's working fine if instead of use returnFirstArg() I do something like this:

when(roomGeneralService.getNameElement(anyString())).thenAnswer(new Answer<String>() {
   @Override
   public String answer(InvocationOnMock invocation) throws Throwable {
      return (String) invocation.getArguments()[0];
   }
});

But I would use returnFirstArg() as in the rest of my tests (tests look cleaner), as well as mocking is working fine if the method getNameElement would receive an String instead a T arg.

Thanks for the help.


Solution

  • I finally decided to open an issue (#1071) on mockito github project and it has been fixed in version 2.8.29 (See the official changelog) Thanks to the Mockito team to solve it so quickly!

    Quoting @ChristianSchwarz, here is a problem explanation:

    Mockito checks if the argument type is compatible with the return type to make a misuse ot the API as soon as posible visible. In this case the argument-type Object is inferred from the generic type T, due to type erasure. Since Object is not a subtype of String Mockito throws the exception you see.

    Solution: The issue can by fixed by inferring the argument-type of the actual argument-instance. In case the argument-type is a primitive or the arg is null Mockito must fallback and use the type provided by method signature.