Repository: mockito/mockito
Issue: Mockito AdditionalAnswers.returnsFirstArg() doesn't work with generic first arg

_Issue concerning [this stackoverflow question](http://stackoverflow.com/questions/30670275/mockito-returnsfirstarg-doesnt-work-with-generic-first-arg).
I've checked it's not working both Mockito 1.x and 2.x._


-------------------



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;
    import org.mockito.runners.MockitoJUnitRunner;
    
    @RunWith(MockitoJUnitRunner.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.

Issue comments:
@troig 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. 

I have a solution ~~that currently doesn't work due to regressions introduced with #1032 (vararg-expansion is broken)~~.

Thanks @ChristianSchwarz for you quick answer!

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java b/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java
index 7c0a144b71..5419819647 100644
--- a/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java
+++ b/src/main/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAt.java
@@ -4,9 +4,12 @@
  */
 package org.mockito.internal.stubbing.answers;
 
+import static org.mockito.internal.exceptions.Reporter.invalidArgumentPositionRangeAtInvocationTime;
+import static org.mockito.internal.exceptions.Reporter.invalidArgumentRangeAtIdentityAnswerCreationTime;
+import static org.mockito.internal.exceptions.Reporter.wrongTypeOfArgumentToReturn;
+
 import java.io.Serializable;
 import java.lang.reflect.Method;
-import org.mockito.internal.exceptions.Reporter;
 import org.mockito.invocation.Invocation;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -15,8 +18,12 @@
 /**
  * Returns the passed parameter identity at specified index.
  * <p>
- * <p>The <code>argumentIndex</code> represents the index in the argument array of the invocation.</p>
- * <p>If this number equals -1 then the last argument is returned.</p>
+ * <p>
+ * The <code>argumentIndex</code> represents the index in the argument array of the invocation.
+ * </p>
+ * <p>
+ * If this number equals -1 then the last argument is returned.
+ * </p>
  *
  * @see org.mockito.AdditionalAnswers
  * @since 1.9.5
@@ -32,12 +39,12 @@ public class ReturnsArgumentAt implements Answer<Object>, ValidableAnswer, Seria
     /**
      * Build the identity answer to return the argument at the given position in the argument array.
      *
-     * @param wantedArgumentPosition The position of the argument identity to return in the invocation.
-     *                               Using <code>-1</code> indicates the last argument ({@link #LAST_ARGUMENT}).
+     * @param wantedArgumentPosition
+     *            The position of the argument identity to return in the invocation. Using <code>-1</code> indicates the last argument ({@link #LAST_ARGUMENT}).
      */
     public ReturnsArgumentAt(int wantedArgumentPosition) {
         if (wantedArgumentPosition != LAST_ARGUMENT && wantedArgumentPosition < 0) {
-            throw Reporter.invalidArgumentRangeAtIdentityAnswerCreationTime();
+            throw invalidArgumentRangeAtIdentityAnswerCreationTime();
         }
         this.wantedArgumentPosition = wantedArgumentPosition;
     }
@@ -50,49 +57,53 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
         if (wantedArgIndexIsVarargAndSameTypeAsReturnType(invocation.getMethod(), argumentPosition)) {
             // answer raw vararg array argument
             return ((Invocation) invocation).getRawArguments()[argumentPosition];
-        } else {
-            // answer expanded argument at wanted position
-            return invocation.getArgument(argumentPosition);
         }
+
+        // answer expanded argument at wanted position
+        return invocation.getArgument(argumentPosition);
+
     }
 
     @Override
     public void validateFor(InvocationOnMock invocation) {
         int argumentPosition = inferWantedArgumentPosition(invocation);
         validateIndexWithinInvocationRange(invocation, argumentPosition);
-        validateArgumentTypeCompatibility(invocation, argumentPosition);
+        validateArgumentTypeCompatibility((Invocation) invocation, argumentPosition);
     }
 
     private int inferWantedArgumentPosition(InvocationOnMock invocation) {
-        return wantedArgumentPosition == LAST_ARGUMENT ?
-               invocation.getArguments().length - 1 :
-               wantedArgumentPosition;
+        if (wantedArgumentPosition == LAST_ARGUMENT)
+            return invocation.getArguments().length - 1;
+
+        return wantedArgumentPosition;
     }
 
     private void validateIndexWithinInvocationRange(InvocationOnMock invocation, int argumentPosition) {
         if (!wantedArgumentPositionIsValidForInvocation(invocation, argumentPosition)) {
-            throw Reporter.invalidArgumentPositionRangeAtInvocationTime(invocation,
-                                                                        wantedArgumentPosition == LAST_ARGUMENT,
-                                                                        wantedArgumentPosition);
+            throw invalidArgumentPositionRangeAtInvocationTime(invocation,
+                                                               wantedArgumentPosition == LAST_ARGUMENT,
+                                                               wantedArgumentPosition);
         }
     }
 
-    private void validateArgumentTypeCompatibility(InvocationOnMock invocation, int argumentPosition) {
+    private void validateArgumentTypeCompatibility(Invocation invocation, int argumentPosition) {
         InvocationInfo invocationInfo = new InvocationInfo(invocation);
-        Class<?> inferredArgumentType = inferWantedArgumentType(invocation, argumentPosition);
-        if (!invocationInfo.isValidReturnType(inferredArgumentType)) {
-            throw Reporter.wrongTypeOfArgumentToReturn(invocation,
-                                                       invocationInfo.printMethodReturnType(),
-                                                       inferredArgumentType,
-                                                       wantedArgumentPosition);
+
+        Class<?> inferredArgumentType = inferArgumentType(invocation, argumentPosition);
+
+        if (!invocationInfo.isValidReturnType(inferredArgumentType)){
+            throw wrongTypeOfArgumentToReturn(invocation,
+                                              invocationInfo.printMethodReturnType(),
+                                              inferredArgumentType,
+                                              wantedArgumentPosition);
         }
     }
 
     private boolean wantedArgIndexIsVarargAndSameTypeAsReturnType(Method method, int argumentPosition) {
         Class<?>[] parameterTypes = method.getParameterTypes();
-        return method.isVarArgs()
-               && argumentPosition == /* vararg index */ parameterTypes.length - 1
-               && method.getReturnType().isAssignableFrom(parameterTypes[argumentPosition]);
+        return method.isVarArgs() &&
+              argumentPosition == /* vararg index */ parameterTypes.length - 1 &&
+              method.getReturnType().isAssignableFrom(parameterTypes[argumentPosition]);
     }
 
     private boolean wantedArgumentPositionIsValidForInvocation(InvocationOnMock invocation, int argumentPosition) {
@@ -106,29 +117,37 @@ private boolean wantedArgumentPositionIsValidForInvocation(InvocationOnMock invo
         return true;
     }
 
-    private Class<?> inferWantedArgumentType(InvocationOnMock invocation, int argumentPosition) {
+    private Class<?> inferArgumentType(Invocation invocation, int argumentIndex) {
         Class<?>[] parameterTypes = invocation.getMethod().getParameterTypes();
+
         // Easy when the method is not a vararg
         if (!invocation.getMethod().isVarArgs()) {
-            return parameterTypes[argumentPosition];
+            Class<?> argumentType = parameterTypes[argumentIndex];
+            Object argumentValue = invocation.getArgument(argumentIndex);
+            // we don't want to return primitive wrapper types
+            if (argumentType.isPrimitive() || argumentValue == null) {
+                return argumentType;
+            }
+
+            return argumentValue.getClass();
         }
 
         // Now for varargs
         int varargIndex = parameterTypes.length - 1; // vararg always last
 
-        if (argumentPosition < varargIndex) {
+        if (argumentIndex < varargIndex) {
             // Same for non vararg arguments
-            return parameterTypes[argumentPosition];
-        } else {
-            // if wanted argument is vararg
-            if (wantedArgIndexIsVarargAndSameTypeAsReturnType(invocation.getMethod(), argumentPosition)) {
-                // return the vararg array if return type is compatible
-                // because the user probably want to return the array itself if the return type is compatible
-                return parameterTypes[argumentPosition]; // move to MethodInfo ?
-            } else {
-                // return the type in this vararg array
-                return parameterTypes[varargIndex].getComponentType();
-            }
+            return parameterTypes[argumentIndex];
         }
+        // if wanted argument is vararg
+        if (wantedArgIndexIsVarargAndSameTypeAsReturnType(invocation.getMethod(), argumentIndex)) {
+            // return the vararg array if return type is compatible
+            // because the user probably want to return the array itself if the return type is compatible
+            return parameterTypes[argumentIndex]; // move to MethodInfo ?
+        }
+        // return the type in this vararg array
+        return parameterTypes[varargIndex].getComponentType();
+
     }
 }
+


Test patch:
diff --git a/src/test/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAtTest.java b/src/test/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAtTest.java
index 8bb0451e0c..3a817cb5a1 100644
--- a/src/test/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAtTest.java
+++ b/src/test/java/org/mockito/internal/stubbing/answers/ReturnsArgumentAtTest.java
@@ -185,6 +185,17 @@ public void should_fail_if_argument_type_of_signature_is_incompatible_with_retur
         }
     }
 
+    @Test
+    public void shouldNotFailWhenArgumentIsGenericAndCompatibleWithReturnType() throws Exception {
+        new ReturnsArgumentAt(0 ).validateFor(
+                new InvocationBuilder().method("genericToString")
+                                       .argTypes(Object.class)
+                                       .args("anyString")
+                                       .toInvocation()
+        );
+    }
+
+
     private static InvocationOnMock invocationWith(Object... parameters) {
         return new InvocationBuilder().method("varargsReturningString")
                                       .argTypes(Object[].class)
diff --git a/src/test/java/org/mockitousage/IMethods.java b/src/test/java/org/mockitousage/IMethods.java
index 2d5663115c..7ec37500d9 100644
--- a/src/test/java/org/mockitousage/IMethods.java
+++ b/src/test/java/org/mockitousage/IMethods.java
@@ -233,4 +233,6 @@ public interface IMethods {
     Integer toIntWrapper(int i);
 
     String forObject(Object object);
+
+    <T> String genericToString(T arg);
 }
diff --git a/src/test/java/org/mockitousage/MethodsImpl.java b/src/test/java/org/mockitousage/MethodsImpl.java
index b9830579e7..9417cbebe5 100644
--- a/src/test/java/org/mockitousage/MethodsImpl.java
+++ b/src/test/java/org/mockitousage/MethodsImpl.java
@@ -451,4 +451,9 @@ public String forObject(Object object) {
     public Void voidReturningMethod() {
         return null;
     }
+
+    @Override
+    public <T> String genericToString(T arg) {
+        return null;
+    }
 }