2

Consider the following "production" code:

public class Foo<T extends Number> {

    private Bar delegate;
    private Class<T> numberClass;

    public Foo(Class<T> numberClass) {
        this.numberClass = numberClass;
    }

    public T doSomething() {
        if (numberClass==Integer.class) {
             return delegate.getIntValue();
        } else if (numberClass==Float.class) {
             return delegate.getFloatValue();
        } else if (numberClass==Double.class) {
             return delegate.getDoubleValue();
        }  // etc etc etc
        return null;
    }
}

This is based on a real situation I'm facing ... I have no control over the implementation of the delegate object.

So, how can I unit test this class with a JMockit @Tested annotation? I can't use @Injectable to inject a Class value... it complains that Class is not mockable. And I'm not seeking to mock it, just inject it. Unlike Strings or primitives or enums, where I could say @Injectable("foobar") String s; and get a non-mocked injectable object, these semantics don't apply for Class objects.

Obviously, my workaround is to hand-construct my Foo in a @Before method, and so I shall. But I can't help but feel like this situation should be possible with JMockit.

dcsohl
  • 7,186
  • 1
  • 26
  • 44

1 Answers1

1

In short, you can't use @Tested because jmockit does not let you mock the Class class. You would have to write your test something like this:

@Test
public void testDoSomethingWithInteger(@Mocked final Bar bar) {
    final int expectedValue = 10;

    new NonStrictExpectations() {
        {
            bar.getIntValue();
            result = expectedValue;
        }
    };

    Foo foo = new Foo(Integer.class);
    assertEquals(expectedValue, foo.doSomething());
}

I believe the reason for the restriction on mocking core java classes is due to the possibly adverse implications that mocking such classes would have on the execution java since the core classes will generally affect a large segment of your code. This SO answer follows a similar line or reasoning.

Or it may have something to do with how classes are loaded in the classloader. I did read a blog post that claimed a hack to mock core classes by starting up your test jvm using these command line arguments: -Xbootclasspath/a:jmockit.jar: -javaagent:jmockit.jar.

Community
  • 1
  • 1
TheEllis
  • 1,666
  • 11
  • 18
  • I understand the restrictions and their reasons. I guess I'm just asking for a way to constructor-inject *without mocking*. Ironically, field-injection would be easier (with `Deencapsulation`)... – dcsohl May 19 '16 at 15:03
  • I would argue that my example above accomplishes constructor injection without mocking. All the `@Tested` and `@Injectable` annotations are doing is instantiating Foo with a constructor that matches the injectable variables and using the value specified. However, injectable variable are actually mocked instances, per the [jmockit api](http://jmockit.org/api1x/mockit/Injectable.html): "Such instances can be said to be proper mock objects, in contrast to the mocked instances of a regular @Mocked type." The only mocking that is being done in my example above is the Bar class. – TheEllis May 19 '16 at 15:11
  • Also, you don't have to even mock the Bar class if you don't want, although that would make the test an integration test rather than a unit test. – TheEllis May 19 '16 at 15:13