Printing Object Ref after "Actual invocation has different argument" when using ArgumentMatcher

3,502 views
Skip to first unread message

sjaak

unread,
Jan 19, 2011, 4:23:57 PM1/19/11
to mockito
Hi,

I want to use a matcher for generated code that does not have a
toString method implemented. However, if I implement a matcher, the
expected object is described nicely (using the describeTo on the
matcher). However, for the actual object, the object ID is printed
(base behavior of toString)

I've made an example class. This is the result:

Argument(s) are different! Wanted:
someInterface.someMethod(
test: MyTestObject2
);
-> at TestCase.test1(TestCase.java:54)
Actual invocation has different arguments:
someInterface.someMethod(
TestCase$TestObject@1abab88
);


How should this work? Wat do I miss? This is the test code I'm using:

--------------------------------------------------------------------------------------------------------------

import org.hamcrest.Description;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import static org.mockito.Mockito.*;

public class TestCase
{

public class TestObject
{

private String itsContent;

public TestObject(String aContent)
{
itsContent = aContent;
}

String getContent()
{
return itsContent;
}

}

@Test
public void test1()
{
/* prepare */
SomeInterface myMock = mock(SomeInterface.class);
TestObject myActual = new TestObject("MyTestObject1");
TestObject myExpected = new TestObject("MyTestObject2");

/* action */
myMock.someMethod(myActual);

/* verify */
verify(myMock).someMethod(eq2(myExpected));
}


/* test class */
public static interface SomeInterface
{
void someMethod(TestObject anObject);
}


/* equals method */
public static <T extends TestObject> T eq2(T anActual)
{
return argThat(new TestObjectMatcher<T>(anActual));
}

/* matcher */
public static class TestObjectMatcher<T extends TestObject>
extends ArgumentMatcher<T>
{
TestObject itsTestObject;

public TestObjectMatcher(TestObject aTestObject)
{
itsTestObject = aTestObject;
}

@Override
public boolean matches(Object anArg)
{
TestObject myTestObject = (TestObject)anArg;
return
itsTestObject.getClass().equals(myTestObject.getContent());
}

@Override
public void describeTo(Description aDescription)
{
aDescription.appendText("test:
"+itsTestObject.getContent());
}

}

--------------------------------------------------------------------------------------------------------------

Thanks and best regards,
Sjaak

szczepiq

unread,
Jan 20, 2011, 4:00:17 PM1/20/11
to moc...@googlegroups.com
Hey,

If you don't have toString() implemented then you won't see your argument printed nicely. What would you expect?

Cheers!
Szczepan


--
You received this message because you are subscribed to the Google Groups "mockito" group.
To post to this group, send email to moc...@googlegroups.com.
To unsubscribe from this group, send email to mockito+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mockito?hl=en.


sjaak

unread,
Jan 21, 2011, 2:22:11 AM1/21/11
to mockito


On 20 jan, 22:00, szczepiq <szcze...@gmail.com> wrote:
> Hey,
>
> If you don't have toString() implemented then you won't see your argument
> printed nicely. What would you expect?
>
> Cheers!
> Szczepan
>
> > mockito+u...@googlegroups.com<mockito%2Bunsu...@googlegroups.com>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/mockito?hl=en.


Hi Szczepan,

Thanks.

Sometimes you don't have the possibility to implement the toString().
For instance, if its not under your control. In this case, its
generated by an ASN.1 compiler code that I want to match. Note: the
example is just to show the problem. Now, there is a print function on
the generated objects.

However, since the code is generated I cannot override the toString().

Best regards,
Sjaak

sjaak

unread,
Jan 21, 2011, 1:31:50 PM1/21/11
to mockito
> Thanks for your answer.
>
> But Sometimes you don't have the possibility to implement the toString().
> For instance, if its not under your control. In this case, its
> generated by an ASN.1 compiler code that I want to match. Note: the
> example is just to show the problem. Now, there is a print function on
> the generated objects. This function I'm using to compare the objects
> and in the describeTo to get a nice print-out of the expected object.
>
> However, since the code is generated I cannot override the toString().
>
I also thought from the description from Hamcrest
http://code.google.com/p/hamcrest/wiki/Tutorial: "For our Matcher
implementation it is most convenient to subclass TypeSafeMatcher,
which does the cast to a Double for us. We need only implement the
matchesSafely method - which simply checks to see if the Double is NaN
- and the describeTo method - which is used to produce a failure
message when a test fails. Here's an example of how the failure
message looks: "


Mockito seems to 'only' prints the expected. You have no control over
the 'comparisson' printout.

> Best regards,
> Sjaak

Graham Allan

unread,
Jan 21, 2011, 2:03:20 PM1/21/11
to moc...@googlegroups.com
> I also thought from the description from Hamcrest
> http://code.google.com/p/hamcrest/wiki/Tutorial: "For our Matcher
> implementation it is most convenient to subclass TypeSafeMatcher,
> which does the cast to a Double for us. We need only implement the
> matchesSafely method - which simply checks to see if the Double is NaN
> - and the describeTo method - which is used to produce a failure
> message when a test fails. Here's an example of how the failure
> message looks: "
>
>
> Mockito seems to 'only' prints the expected. You have no control over
> the 'comparisson' printout.
>
> > Best regards,
> > Sjaak

Hey Sjaak,

The problem comes down to an incompatibility between JUnit and Hamcrest
version 1.2 and above[1]. JUnit uses the default toString() whereas Hamcrest
uses the describeMismatch() when tests make calls to:
org.hamcrest.MatcherAssert.assertThat()
rather than:
org.junit.Assert.assertThat()

The TypeSafeDiagnosingMatcher you mention is also available only with Hamcrest
version 1.2 and above.

JUnit is currently not planning to upgrade above Hamcrest 1.1 because of
incompatibility in method signatures, mostly related to generics bounds. Maybe
Szczepan could tell us if Mockito is subject to the same conditions?

If Mockito has the same compatibility issue as JUnit, I don't see there being
an easy solution, I'm afraid :-(

HTH,
Graham


[1] http://stackoverflow.com/questions/3915635/is-there-a-version-of-junit-
assertthat-which-uses-the-hamcrest-describemismatch

sjaak

unread,
Jan 22, 2011, 4:03:45 AM1/22/11
to mockito
Hi Gaham,

First of all, thanks. This clarifies matters a lot. (I'm trying to get
a good printout for some time now). Its waiting for the opinion of
Szczepan on this issue then and to see if there's an elegant solution
to this problem.

In the mean time, I did some experimenting myself. One of the things I
tried is to spy on the argument anActual (on the toString method) and
to return the desired printout. Like this:

/* equals method */
public static <T extends TestObject> T eq2(T anActual)
{
T mySpy = spy(anActual);
when(mySpy.toString()).thenReturn(anActual.getContent());
return argThat(new TestObjectMatcher<T>(mySpy));
}

However Mockito does not allow a spy on this position (which I can
imagine). Although I think that the exception text does not match the
cause ;-).

test1(TestCase) Time elapsed: 0.358 sec <<< ERROR!
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at TestCase.test1(TestCase.java:53)

Example of correct verification:
verify(mock).doSomething()

Also, this error might show up because you verify either of: final/
private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.

I thought about using the java.lang.reflect.Proxy, but that only takes
interrfaces (not classes)...

Do you see another solution?

Best regards
Sjaak

Graham Allan

unread,
Jan 22, 2011, 9:54:06 AM1/22/11
to moc...@googlegroups.com
> Do you see another solution?
>
> Best regards
> Sjaak

The only thing that springs to mind is to wrap the type you have no control
over with something of your own that delegates every call to the real
instance. Then you could implement whatever toString() you want.

That does have downsides, such as a lot of boilerplate, and instanceof won't
work, but it has its benefits as well, like decoupling from 3rd party
dependencies. See also the advice "don't mock types you don't own".

Sorry I couldn't be of more help.

~ Graham

sjaak

unread,
Jan 22, 2011, 10:08:52 AM1/22/11
to mockito
Hi Graham,

No worries. I reckon that generated code is a kind of grey area
(considering "code you don't own" ;-) ).

Thanks a lot anyway .

//
Sjaak

szczepiq

unread,
Jan 22, 2011, 5:16:11 PM1/22/11
to moc...@googlegroups.com
>org.mockito.exceptions.
misusing.UnfinishedVerificationException:

Perhaps your toString() is final? :)

>TypeSafeDiagnosingMatcher

I'm not too keen on enabling it. First, it requires hamcrest 1.2 and if I ship mockito with it folks may encounter some problems (as mentioned - the incompatibilities). Secondly, I'm not sure if people would use that matcher anyway.

Cheers
Szczepan

--
You received this message because you are subscribed to the Google Groups "mockito" group.
To post to this group, send email to moc...@googlegroups.com.
To unsubscribe from this group, send email to mockito+u...@googlegroups.com.

sjaak

unread,
Jan 24, 2011, 5:36:34 AM1/24/11
to mockito
Hi Szczepan,

Thanks!

Nope, the toString() method is not final. I just verified by
overriding the toString().. So something else must cause this
exception. So, something different altogether must be causing the
exception.

I can (of course) understand your not keen on introducing Hamcrest 1.2
if that introduces downward imcompatible changes. I would not do that
myself either.

However, I don't agree there are no usage's of such functionality (of
course, since I'm stakeholder here ;-) ). I mean, there must be a
reason why the autors of Hamcrest found this to be usefull. I
personally, like to deliver my API's with a separate (maven) project,
containing matchers and a set of default test vectors, to simplify /
encourage unit testing.

But I do see a lot of more use cases. Especially when you are
comparing collections, it would be very usefull to print something
like: "elements in set match, appart from element 'x' in expected
collection, which is superflous". Or "2 elements differ: element X:
'x' and element Y: 'y').." etc.

Perhaps there is a possibility to have a pre-Hamcrest-1.2 extension
including only this Matcher, or have your own Mockito specific
implementation?

Anyway, if there are more folks out there in need of such
functionality, please react ;-).

Best regards,
Sjaak

On 22 jan, 23:16, szczepiq <szcze...@gmail.com> wrote:
> >org.mockito.exceptions.
>
> misusing.UnfinishedVerificationException:
>
> Perhaps your toString() is final? :)
>
> >TypeSafeDiagnosingMatcher
>
> I'm not too keen on enabling it. First, it requires hamcrest 1.2 and if I
> ship mockito with it folks may encounter some problems (as mentioned - the
> incompatibilities). Secondly, I'm not sure if people would use that matcher
> anyway.
>
> Cheers
> Szczepan
>
> > mockito+u...@googlegroups.com<mockito%2Bunsu...@googlegroups.com>
> > .

ErikEngerd

unread,
Jan 24, 2011, 10:40:01 AM1/24/11
to mockito
Do I understand correctly that in the message below, the first part is
generated by the matcher and the second part fully by junit?

Generated by the matcher?

Argument(s) are different! Wanted:
someInterface.someMethod(
test: MyTestObject2
);
-> at TestCase.test1(TestCase.java:54)

Generated by junit?

Actual invocation has different arguments:
someInterface.someMethod(
TestCase$TestObject@1abab88
);

In that case, indeed it is junit functionality to perform a custom
toString().

Apart from this, if an issue like this occurs in your code, you can
always use an ArgumentCaptor to capture the argument and match in
detail after that. In many cases, I prefer using an ArgumentCaptor
over a custom matcher.

Also, you can write a custom stub either manually or through mockito
stubbing and do the assertions you want inside the stub.


sjaak

unread,
Jan 23, 2011, 4:15:57 AM1/23/11
to mockito
Hi Szczepan,

Thanks. I completely understand that you want to be backwards
comaptible.

However (of course ;-) ), I don't agree that there would not be a use
for it. I mean, the authors of Hamcrest saw a need for it as well.
What I try to do when I develop API's is to deliver Argument Matchers
with that API (as a separate test library) as well (just as some pre-
defined test vectors to use). This way, users of that API are
encouraged & have a lower threshold to actually do unit test.

My situation might be particular (trying to mock classes that are not
owned by myself). But I've also encountered situations where I'm
comparing collections of my own datatypes. Then you typically want to
print something like element x occuring in expected collection does
not match x' in actual collection (at least, if all the other elements
match). And this is only one use case, but I think there are many
more.

Perhaps worthwile to consider a (private) pre-hamcrest-1.2 extension
(only considering this particular matcher)? If can help if needed
(although I'm surely not an expert)..?

Also: perhaps there are more people out there that encounter similar
problems?

Best regards,
Sjaak

On 22 jan, 23:16, szczepiq <szcze...@gmail.com> wrote:
> >org.mockito.exceptions.
>
> misusing.UnfinishedVerificationException:
>
> Perhaps your toString() is final? :)
>
> >TypeSafeDiagnosingMatcher
>
> I'm not too keen on enabling it. First, it requires hamcrest 1.2 and if I
> ship mockito with it folks may encounter some problems (as mentioned - the
> incompatibilities). Secondly, I'm not sure if people would use that matcher
> anyway.
>
> Cheers
> Szczepan
>
> > mockito+u...@googlegroups.com<mockito%2Bunsu...@googlegroups.com>
> > .
Reply all
Reply to author
Forward
0 new messages