After a fair amount of looking at existing code and manipulating things in pieces, I figured out what was going on.
First, we'll take a step back to where this matcher is called in the EasyMock 1 code:
myMock.find("Testing", new DateSearchCriteria());
myControl.setMatcher(new GenericMatcher());
myControl.setReturnValue(AppConstants.TODAY);
Two things here: first there are two arguments for the find method. Second, there is only one matcher being assigned. With a bit of trial and error, I discovered that the EasyMock 1 ArgumentMatcher
version of matches() takes an array because it's comparing all of the arguments in the call. So in this case, Object[] expected = ["Testing", new DateSearchCriteria()]
. The custom matcher in the example from the question checks if the first argument is equal and the second argument has the same name, with an implicit understanding that the first argument will be a String and the second will be a DateSearchCriteria.
This isn't a great way to implement a matcher because if you do any refactoring, like changing the method signature or changing the implementation of DateSearchCriteria, the matcher will break. But since EasyMock 1 only let you set one matcher per method, this was the only way to match things.
EasyMock 2 and 3 improves the functionality so that you can set a matcher for each individual argument, rather than once for the whole method. This is why anIArgumentMatcher
matches() now just takes an Object, rather than an Object array; because it's only examining one argument rather than all of them at once. So the EasyMock 1 code above would be as follows in EasyMock 2/3:
expect(persistenceManager.find(eq("Testing"), eqName(new DateSearchCriteria())))
.setReturnValue(AppConstants.TODAY));
The eq() method is a matcher for equality built into EasyMock. The eqName() method would be a custom method implemented like so:
public <T> T eqName(T in) {
reportMatcher(new NameMatcher(in));
return null;
}
And NameMatcher
would be implemented by making the same check that the old matcher did on argument #1:
public class NameMatcher implements IArgumentMatcher {
private Object expected;
public NameMatcher(Object expected) {
this.expected = expected;
}
@Override
public void appendTo(StringBuffer buffer) {
buffer.appendTo("Name is \"" + expected.getName() + "\"");
}
@Override
public boolean matches(Object actual) {
return expected.getName().equals(actual.getName());
}
}
So to sum it all up, a multi-argument matches() method like in EasyMock 1 where it is doing comparisons on each element of the inputted array is in fact checking each argument of the method that is being mocked. It is making assumptions about the state of each argument and is prone to breaking if you refactor. EasyMock 2 instead makes matchers per-argument instead of per-method. So what you need to do to convert an EasyMock 1 ArgumentsMatcher
to an EasyMock 2/3 IArgumentMatcher
is to basically split apart each argument match into its own matcher. In the example above, the old matcher tested for equality on argument 0 and an equal name on argument 1. So instead when you declare the method mock, you put an equality matcher on argument 0 (which is built in to EasyMock 2/3) and create your own name matcher for argument 1. They don't share matchers, they are individual, self-contained matchers.