History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: JMOCK-122
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Nat Pryce
Reporter: Steve Gilbert
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
jMock

Method arguments are not matched when mocking a concrete Comparator implementation

Created: 09/May/07 10:03 AM   Updated: 22/May/07 02:06 AM
Component/s: Library
Affects Version/s: 2.1.0-RC3
Fix Version/s: 2.1.0

Time Tracking:
Not Specified

File Attachments: 1. Java Source File ComparatorTest.java (2 kb)


Testcase included: yes


 Description  « Hide
When attempting to mock a concrete Comparator implementation, when compare is called, the method is not matched and the test fails with a message like this:

java.lang.AssertionError: unexpected invocation: myComparator.compare(<ComparatorTest$MyComparable@471e30>, <ComparatorTest$MyComparable@10ef90c>)
expectations:
expected exactly 1 time, already invoked 0 times: myComparator.compare(<ComparatorTest$MyComparable@471e30>, <ComparatorTest$MyComparable@10ef90c>); returns <0>
at org.jmock.internal.InvocationDispatcher.dispatch(InvocationDispatcher.java:54)
at org.jmock.Mockery.dispatch(Mockery.java:194)
at org.jmock.Mockery.access$000(Mockery.java:34)
at org.jmock.Mockery$MockObject.invoke(Mockery.java:221)
at org.jmock.internal.InvocationDiverter.invoke(InvocationDiverter.java:27)
at org.jmock.internal.ProxiedObjectIdentity.invoke(ProxiedObjectIdentity.java:33)
at org.jmock.lib.legacy.ClassImposteriser$2.invoke(ClassImposteriser.java:75)
at ComparatorTest$MyComparator$$EnhancerByCGLIB$$890ceb30.compare(<generated>)
at ComparatorTest.compare(ComparatorTest.java:53)
at ComparatorTest.failingTest(ComparatorTest.java:32)

The message seems to indicate the arguments are identical, however, they are not.

I've attached a unit test with a passing test case and a failing test case. The only difference between the two is how the Comparator is declared. In the passing case, it is defined as "Comparafor" and in the failing test case, it is defined as "MyComparator". I ran into this problem when trying to use Collections.sort from the JDK, which is why I have created a "compare" method in the test class, which simulates what happens inside of the JDK code – the comparator is passed in the same way as in my "compare" method. When the compare method is called, the Method in the invocation has Object arguments and the expectation is for MyComparable arguments. This is what causes the test failure in the failing case. It seems the Method arguments are coming from the Comparable interface in one case and my real implementation in the other. I am fairly new to Generics and don't know if this is a jMock problem, a CGLIB problem, or something else.

It would be helpful if the message indicated that the arguments of the invocation don't match those in the expectations. The mismatch is happening in MethodMatcher, before parameter checking happens (in ParameterMatcher?), so it wasn't clear where or how the message could be improved easily.



 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Nat Pryce - 10/May/07 11:19 PM
Wow. This brought to light a really tricky implementation detail of Java generics. Basically, your test is capturing an expectation of a fully typed implementation of a generic method [int MyComparator.compare(String,String)] but when invoked through a generic interface a "bridge" method is invoked [int MyComparator.compare(Object,Object)]. That bridge method is generated by the compiler to do run-time type checking and is not visible at compile time, only at runtime when using reflection.

In my opinion it's actually a bug in CGLIB. The Enhancer shouldn't be overriding bridge methods.

However, I think I have a workaround.


Nat Pryce - 10/May/07 11:26 PM
I've committed a fix to CVS