Search code examples
javaunit-testingmockingjmockit

Using jmockit expectations with matchers and primitive types


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?


Solution

  • 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.