java_linux_user
java_linux_user

Reputation: 53

Why doesn't dynamic partial mocking work for JMockit's @Injectable?

In the following test case where no Expectations have been recorded, I would expect that the dynamic partial mocking feature will be used for the fields A and B which are initialized in UnitToTest using @Injectable. But instead always the method calls are mocked. Only using an invalid filter value for static partial mocking, it is possible to call the real methods:

@Service
class A {
 public String doSomething() {  return "doSomething";   }
 public String doSomethingElse() {  return "doSomethingElse";   }
}

@Service
class B {
 public String doSomething() {  return "doSomething";   }
 public String doSomethingElse() {  return "doSomethingElse";   }
}

@Service
class UnitToTest {
 @Autowired B b;
 @Autowired A a;
 public B getB() {  return b;   }
 public A getA() {  return a;   }
}

public class TestClass {
 @Tested    UnitToTest unit;
 // @Mocked({ "someInvalidFilter()" })
 @Injectable    A a;
 // @Mocked({ "someInvalidFilter()" })
 @Injectable    B b;

 @Test
 public void test() {
    // actual return value is always null if no invalid static partial
    // mocking filters are specified above
    assertEquals("doSomething", unit.getA().doSomething());
    assertEquals("doSomethingElse", unit.getA().doSomethingElse());
    assertEquals("doSomething", unit.getB().doSomething());
    assertEquals("doSomethingElse", unit.getB().doSomethingElse());
 }
}

For me it looks like dynamic partial mocking with JMockit doesn't work for @Injectables. Is that a known restriction?

Upvotes: 1

Views: 2720

Answers (1)

Rogério
Rogério

Reputation: 16390

@Injectables always get injected into @Tested objects, assuming a matching field or constructor parameter can be found; the injection process even takes into consideration DI annotations such as @Inject and @Autowired.

However, an @Injectable instance is always created as an uninitialized (ie, with no state) and fully mocked instance. Partial mocking, on the other hand, is meant for real instances that you instantiate (and initialize) yourself in the test.

So, what you seem to be asking for is that said real instances (partially mocked or not) could be injected into @Tested objects. Indeed, this is not supported (except by calling Deencapsulation.setField), since a motivating use case was never presented by users.

That said, the example test will pass if it is changed to the following:

public class TestClass {
    @Tested(fullyInitialized = true) UnitToTest unit;

    @Test
    public void test() {
        assertEquals("doSomething", unit.getA().doSomething());
        assertEquals("doSomethingElse", unit.getA().doSomethingElse());
        assertEquals("doSomething", unit.getB().doSomething());
        assertEquals("doSomethingElse", unit.getB().doSomethingElse());
    }
}

The above is an integration test, though, not a unit test.

Upvotes: 1

Related Questions