jMock

Method arguments are not matched when mocking a concrete Comparator implementation

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 2.1.0-RC3
  • Fix Version/s: 2.1.0
  • Component/s: JMock 2.x.x Library
  • Labels:
    None
  • Testcase included:
    yes
  • Number of attachments :
    1

Description

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.

Activity

Hide
Nat Pryce added a comment -

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.

Show
Nat Pryce added a comment - 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.
Hide
Nat Pryce added a comment -

I've committed a fix to CVS

Show
Nat Pryce added a comment - I've committed a fix to CVS

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: