joepadde / mockito

Automatically exported from code.google.com/p/mockito
0 stars 0 forks source link

overloaded methods with vararg variants cause problem #415

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
<code>
package org.test.mockito;

import java.util.Collection;

import org.junit.Test;
import org.mockito.Mockito;

public class MockitoTest extends Mockito {
    ToTest toTestMock = mock(ToTest.class);
    GenericHelper<Collection> listMock = mock(GenericHelper.class);

    class GenericHelper<T> {
        T t;

        T getT() {
            return t;
        }
    }

    class ToTest {
        String mymethod(Object... os) {
            return "Object array, size: " + os.length;
        }

        String mymethod(GenericHelper<?>... ts) {
            return "GenericHelper array, size: " + ts.length;
        }

        String mymethod(GenericHelper<Collection<?>> gh) {
            return "GenericHelper, value: " + gh.getT();
        }

        String mymethod(Collection<?> collection) {
            return "collection, size: " + collection.size();
        }

    }

    @Test
    public void test() {
        GenericHelper<Collection> castedWithGeneric = listMock;
        //when(toTestMock.mymethod(any(GenericHelper.class))).thenReturn(" Mocked method called!"); // returns null
        //when(toTestMock.mymethod(any())).thenReturn(" Mocked method called!"); // returns null
        when(toTestMock.mymethod(eq(castedWithGeneric))).thenReturn(" Mocked method called!"); //works
        //when(toTestMock.mymethod(castedWithGeneric)).thenReturn(" Mocked method called!"); //works
        String s = toTestMock.mymethod(listMock);
        System.out.println(s);
    }

}
</code>
Above test code fails when the lines marked with null is commented in to work.
The general problem is, that by mocking a class, that has several overloaded 
methods, including some with varargs parameter, and in the above generic 
structure, the type based argument matchers seems don't work.

Expected output would be the "Mocked method called" in all lines, but with 
those remarked with null, null will return - method is not stubbed as it should 
have.

What version of the product are you using?  1.8.4

On what operating system? Win7

I found the problem during JPA Criteria API mocking, the actual mocked object 
was the ExpressionImpl object, the method is the in() method, that has 4 
overloaded variants, from which 2 uses vararg. Inside the Mockito code 
InvocationMatcher.hasSameMethod showed, that while i was calling with the 
Expression parameter method, the candidate's type was Expression[], and this 
unmatch led to returning null. I will stay contact, and give additional 
information as needed.

Original issue reported on code.google.com by Attila.P...@gmail.com on 28 Jan 2013 at 4:14

GoogleCodeExporter commented 8 years ago
Hi,

I'm not sure what your problem is, or if there's a real issue with Mockito but 
here's the Java compiler sees things.

when(toTestMock.mymethod(any(GenericHelper.class))).thenReturn(" Mocked method 
called!");
-> will be compiled to :  INVOKEVIRTUAL MockitoTest$ToTest.mymethod 
(LMockitoTest$GenericHelper;)Ljava/lang/String;

when(toTestMock.mymethod(any())).thenReturn(" Mocked method called!");
-> will be compiled to :  INVOKEVIRTUAL MockitoTest$ToTest.mymethod 
([Ljava/lang/Object;)Ljava/lang/String;

when(toTestMock.mymethod(eq(castedWithGeneric))).thenReturn(" Mocked method 
called!");
-> will be compiled to :  INVOKEVIRTUAL MockitoTest$ToTest.mymethod 
([LMockitoTest$GenericHelper;)Ljava/lang/String;

However the line 
toTestMock.mymethod(listMock)
-> will be compiled to :  INVOKEVIRTUAL MockitoTest$ToTest.mymethod 
([LMockitoTest$GenericHelper;)Ljava/lang/String;

Which is the same as the third stub. If your actual invocation gets compiled to 
one of the other form it'll work as expected. Unfortunately we don't really 
want to greedly stub overloaded declaration.

Also note that at the moment the javadoc of 'any' says it doesn't do any type 
checks, it's a short version for 'anything'. Too bad many people mistake that 
with isA. We will certainly change that later.

Hope that helps a bit.

Original comment by brice.du...@gmail.com on 29 Jan 2013 at 9:35

GoogleCodeExporter commented 8 years ago
I have consulted with a more experienced colleague here, and it turned out that 
the issue is caused by how generic works in Java. It seems that this problem is 
not related to Mockito.

this definition:
GenericHelper<Collection> castedWithGeneric = listMock;

does not match the intended method's parameter:
String mymethod(GenericHelper<Collection<?>> gh)

because GenericHelper<Collection<?>> does not extend GenericHelper<Collection>
this relation is only true between the Collection - Collection<?>

so, the compiler picks up the next relevant method, the
String mymethod(GenericHelper<?>... ts) 

Mockito assigns the stub to this method, which does not match the actual call's 
parameter.

I should have defined the mocked object as:
GenericHelper<Collection<?>> castedWithGeneric = mock(GenericHelper.class);

Original comment by Attila.P...@gmail.com on 30 Jan 2013 at 11:01

GoogleCodeExporter commented 8 years ago
OK, glad you overcame your problem.

Don't hesitate to go to the mailing list if you are unsure if this is a bug 
with mockito ;)

Cheers
Brice

Original comment by brice.du...@gmail.com on 30 Jan 2013 at 11:17