views:

1117

answers:

3

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?

+1  A: 

the issue is the combination of Expectation usage and that Matchers does not support primitive type.

The Matchers code rely on Generic which basically does not support primitive type. Typically the usage of Matchers is more for matching value; with the auto-boxing/unboxing feater in Java 5, this is usually not a problem.

But JMockit's Expectation is not using it for matching value, it uses it for some kind of parsing to determine the method call signature type..which in this case the Matchers will resulted in Boolean type while your method is primitive type..so it fails to mock it properly.

I'm sorry that I can not tell you any workaround for this. Maybe somebody else can help.

DJ
Hmm, that's what I was afraid of :(Thanks for the answer
Kris Pruden
A: 

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.

Kris Pruden
A: 

I changed JMockit (release 0.982) so that "with(is(false))" and other similar variations now work as expected (it no longer returns null, but the actual argument value inside the inner matcher).

Rogerio