I'm using jmockit for unit testing (with TestNG), and I'm having trouble using the Expectations class to mock out a method that takes a primitive type (boolean) as a parameter, using a matcher. Here's some sample code that illustrates the problem.
/******************************************************/
import static org.hamcrest.Matchers.is;
import mockit.Expectations;
import org.testng.annotations.Test;
public class PrimitiveMatcherTest {
private MyClass obj;
@Test
public void testPrimitiveMatcher() {
new Expectations(true) {
MyClass c;
{
obj = c;
invokeReturning(c.getFoo(with(is(false))), "bas");
}
};
assert "bas".equals(obj.getFoo(false));
Expectations.assertSatisfied();
}
public static class MyClass {
public String getFoo(boolean arg) {
if (arg) {
return "foo";
} else {
return "bar";
}
}
}
}
/******************************************************/
The line containing the call to invokeReturning(...) throws a NullPointerException.
If I change this call to not use a matcher, as in:
invokeReturning(c.getFoo(false), "bas");
it works just fine. This is no good for me, because in my real code I'm actually mocking a multi-parameter method and I need to use a matcher on another argument. In this case, the Expectations class requires that all arguments use a matcher.
I'm pretty sure this is a bug, or perhaps it's not possible to use Matchers with primitive types (that would make me sad). Has anyone encountered this issue, and know how to get around it?
So the problem appears to be in Expectations.with():
protected final <T> T with(Matcher<T> argumentMatcher)
{
argMatchers.add(argumentMatcher);
TypeVariable<?> typeVariable = argumentMatcher.getClass().getTypeParameters()[0];
return (T) Utilities.defaultValueForType(typeVariable.getClass());
}
The call to typeVariable.getClass() does not do what the author expects, and the call to Utilities.defaultValueFor type returns null. The de-autoboxing back the the primitive boolean value is where the NPE comes from.
I fixed it by changing the invokeReturning(...) call to:
invokeReturning(withEqual(false)), "bas");
I'm no longer using a matcher here, but it's good enough for what I need.