I'm looking for a way in JMockit to inject the private fields inside a class while maintaining the ability to trigger the real methods. I use @Injectable
and @Tested
offered by JMockit. But somehow after that the injected instance is not able to call the real method.
Example test:
public class TestClass {
public static class DoSomething {
private Call callee;
public void execute() {
callee.call();
}
}
public static class Call {
public void call() {
System.out.println("real");
}
}
@Tested DoSomething doSomething;
@Injectable Call call;
// nothing happens
@Test
public void testRealCall() {
doSomething.execute();
}
// invocation doesn't help either
@Test
public void testRealCallSecondTry() {
new MockUp<Call>() {
@Mock
@SuppressWarnings("unused")
public void call(Invocation inv) {
inv.proceed();
}
};
doSomething.execute();
}
// this works, but requires redundant methods
@Test
public void testRealCallThirdTry() {
new MockUp<Call>() {
@Mock
@SuppressWarnings("unused")
public void call() {
System.out.println("real");
}
};
doSomething.execute();
}
@Test
public void testFakeCall() {
new MockUp<Call>() {
@Mock
@SuppressWarnings("unused")
public void call() {
System.out.println("fake");
}
};
doSomething.execute();
}
}
Here DoSomething
wraps the Call
instance, which provides a way to print a message. The ideal output of the four test cases would be:
real
real
real
fake
However the actual scenario is that only 3 and 4 worked, printing:
real
fake
This shows if an instance is created using @Injectable
. It's not able to directly call the original method without copying and pasting the old method body to the mocked version. That seems really awkward. Is there a workaround of this?
My understanding is that if you use @Injectable you just get an empty mock and then you can no longer call the original method.
The workaround that I would use is to do the injection "manually" like this:
public class TestClass {
public static class DoSomething {
private Call callee;
public void execute() {
callee.call();
}
}
public static class Call {
public void call() {
System.out.println("real");
}
}
@Tested DoSomething doSomething;
//@Injectable Call call;
// nothing happens
@Test
public void testRealCall() {
Deencapsulation.setField(doSomething, "callee", new Call());
doSomething.execute();
}
// invocation doesn't help either
@Test
public void testRealCallSecondTry() {
new MockUp<Call>() {
@Mock
@SuppressWarnings("unused")
public void call(Invocation inv) {
inv.proceed();
}
};
Deencapsulation.setField(doSomething, "callee", new Call());
doSomething.execute();
}
// this works, but requires redundant methods
@Test
public void testRealCallThirdTry() {
new MockUp<Call>() {
@Mock
@SuppressWarnings("unused")
public void call() {
System.out.println("real");
}
};
Deencapsulation.setField(doSomething, "callee", new Call());
doSomething.execute();
}
@Test
public void testFakeCall() {
new MockUp<Call>() {
@Mock
@SuppressWarnings("unused")
public void call() {
System.out.println("fake");
}
};
Deencapsulation.setField(doSomething, "callee", new Call());
doSomething.execute();
}
}