Tuesday, July 31, 2007

EasyMock2 quirk...

When using EasyMock 2 for testing, typically we need to set up expectations before replay, like this:
expect(mockEmployeeRepository
.findByFirstNameAndLastName("John", "Doe"))
.andReturn(employees);
Sometimes, you don't know exactly what parameter will be used for the expected call, you can instead specify the class of the parameter, such as:
expect(mockEmployeeRepository
.findBySpecification(isA(EmployeeSearchSpecification.class))
.andReturn(employees);
What if you know the exact value of some but not all parameters? I tried the following:
expect(mockEmployeeRepository
.findByDepartmentAndSpecification("HR",
isA(EmployeeSearchSpecification.class))
.andReturn(emplooyees);
Unfortunately, running this test will get the following exception thrown by EasyMock:
java.lang.IllegalStateException: 2 matchers expected, 1 recorded.
The correct way is to wrap the known parameter with an "eq" matcher:
expect(mockEmployeeRepository
.findByDepartmentAndSpecification(eq("HR"),
isA(EmployeeSearchSpecification.class))
.andReturn(employees);
This is a small quirk when using EasyMock 2.2...

18 comments:

Unknown said...

Thanks Alex. That was precisely the issue I was facing today. Is a shame these quirks are not listed as part of the EasyMock documentation.

Alex Wei said...

Thanks Matt. I'm glad that this post helped.

Anonymous said...

This tip was really useful. Thanks.

Anonymous said...

hi I am facing some similar kind of Exception, but in my case there is only parameter for the mocked method

expect(someMock.someMethod(isA(SomeObject.class))).andStubAnswer(same(new IAnswer<List<SomeReturnObject>>() {
public List<SomeReturnObject> answer() throws Throwable {
Object args[] = getCurrentArguments();
SomeObject ca = (SomeObject)args[0];
return new ArrayList<SomeReturnObject>(ca.getSomeReturnObjects());
}
}));

java.lang.IllegalStateException: 1 matchers expected, 2 recorded.
at org.easymock.internal.ExpectedInvocation.createMissingMatchers(ExpectedInvocation.java:41)


any ideas?

Anonymous said...

hi, I removed same method call

expect(someMock.someMethod(isA(SomeObject.class))).andStubAnswer(new IAnswer<List<SomeReturnObject>>() {
public List<SomeReturnObject> answer() throws Throwable {
Object args[] = getCurrentArguments();
SomeObject ca = (SomeObject)args[0];
return new ArrayList<SomeReturnObject>(ca.getSomeReturnObjects());
}
});



java.lang.IllegalStateException: 1 matchers expected, 2 recorded.

Alex Wei said...

Hi anon, unfortunately I have never encountered the situation where the number of recorded matchers is greater than that of expected matchers. Without more code from your test case and the class under test, it is virtually impossible to figure out the cause of this problem...

Anonymous said...

Thanks so much for making this information available -- it helped me to quickly get past a problem which had me really stumped, and I can only guess how many hours it would have taken me to figure this out on my own. Kudos!

Anonymous said...

Excellent. What a strange quirk. Thank you.

Vincenzo Vitale said...

Great man,

you solved my problems!!!



Thanks,
V.

Anonymous said...

Thanks Alex.
I had the same problem and spend quite some time before I found your blog.

Unknown said...

Let me give you a even stranger quirk:

Suppose you want to expect a call with two params, where you must use isA, but the other parameter is null.
Suppose the signature looks like this:
methodName(MyOtherObject moo, MyObject mo);
Then the mock should look like this:
mock.methodName(null, isA(MyObject.class));

But that isn't supported as if you use a matcher, all arguments must use matcher. Ah, then this should work:
mock.methodName(isNull(), isA(MyObject.class));

But no, this doesn't compile unless the signature of methodName use Object as first parameter, which it seldom does.

So, heres the extremely ugly workaraound I found:
mock.methodName(not(isA(MyOtherObject.class)), isA(MyObject.class));

If someone has a neater solution, I'd be thrilled.

Alex Wei said...

Hi Tomas,
Thanks for sharing your experience and your solution!
Alex

Anonymous said...

Tomas, try

mock.methodName((MyOtherObject)isNull(), isA(MyObject.class));

All,

I also have the same problem as the poster that has more recorded matchers than expected. the method i'm mocking has no arguments and returns a boolean, but if i debug the EasyMock code two instanceOf(Long.class) matchers have been recorded! I think this is a threading issue.

Anonymous said...
This comment has been removed by a blog administrator.
Ramón said...

After so long, your solution is still helping! Thanks very much!!!

Anonymous said...

Thanks! Just ran into this issue.

James said...

i have a similar unsolved problem.
my mock statement is :
EasyMock.expect(slotManager.addSlotPageletBinding(EasyMock.isA(String.class), EasyMock.isA(String.class), EasyMock.isA(helloWorld.class))).andReturn(true);

this causes:
3 matchers expected, 4 recorded.

Not able find the problem.
before this statement i have the mock for the overloaded method as:
EasyMock.expect(slotManager.addSlotPageletBinding(EasyMock.isA(String.class),EasyMock.isA(String.class))).andReturn(true).anyTimes();

Carta said...

This has to do with using an argument matcher outside of EasyMock context

http://jira.codehaus.org/browse/EASYMOCK-38