'How to call real methods in class instances annotated with @Injectable in JMockit?
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?
Solution 1:[1]
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();
}
}
Solution 2:[2]
I ran into this question when I had the same problem. However, the existing answer don't work with newer versions of JMockit.
If a field in the tested class is annotated with @Inject, a corresponding @Injectable is required in the test class. Usually. This means that removing the @Injectable and instead mock the class with MockUp suggested in the other answer doesn't work. JMockit will complain with "Missing @Injectable for field ...".
What needs to be done instead is to change the @Injectable annotation to a @Tested annotation, i.e. change this
@Injectable Call call;
to
@Tested Call call;
call becomes a real instance and JMockit doesn't complain about a missing @Injectable. If you need to mock some methods in call, it can be done with MockUp as usual.
new MockUp<Call>() {
@Mock
public void someMethodToMock() {
}
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ignacio Martin |
| Solution 2 |
