Real implementation gets run on mocked collaborator constructor
pedorro opened this issue · 3 comments
Please provide the following information:
- Version of JMockit that was used: 1.49
- JDK version: OpenJDK Runtime Environment Corretto-11.0.5.10.1 (build 11.0.5+10-LTS)
- Description of the problem or enhancement request:
Given very particular circumstances, the real implementation of a mocked collaborator's constructor is getting run. This has the effect of both triggering side-effects from the collaborator's constructor, and propagating possible exceptions from it. It appears that the necessary circumstance is where a collaborator's constructor, invokes another of the same collaborator's constructors, while invoking a static method from a different collaborator. I know, it's convoluted.
The following failing Junit4 test demonstrates this issue:
import mockit.Mocked;
import org.junit.Test;
import static org.junit.Assert.*;
public class FailingMockedCollaboratorTest {
private static boolean sideEffect = false;
@Test
public void testCollaborator (@Mocked Collaborator collaborator) {
boolean caught = false;
try {
new Collaborator();
} catch (IllegalStateException ex) {
caught = true;
}
assertFalse(sideEffect || caught);
}
public static class Collaborator {
public Collaborator () {
this(OtherCollaborator.getValue());
}
public Collaborator (String string) {
}
}
public static class OtherCollaborator {
public static String getValue() {
sideEffect = true;
throw new IllegalStateException("BOOM!");
}
}
}
- Check the following:
-
If a defect or unexpected result, JMockit project members should be able to reproduce it.
For that, include an example test (perhaps accompanied by a Maven/Gradle build script) which
can be executed without changes and reproduces the failure. -
If an enhancement or new feature request, it should be justified by an example test
demonstrating the validity and usefulness of the desired enhancement or new feature. -
The issue does not fall outside the scope of the project (for example, attempting to use
JMockit APIs from Groovy or Scala code, or with an Android runtime). -
The JDK where the problem occurs is a final release, not a development build.
This is just the way it is. When a constructor is mocked, only the code in its body (after the necessary this(...)
or super(...)
call to another constructor) actually gets mocked away.
Hmm..... FWIW, this used to work. I only found this issue when attempting to upgrade a set of existing tests from JMockit v1.38 to v1.49.
To be clear, I really do appreciate your work on JMockit. It is definitely the most capable mocking framework I've used. And while I understand your fondness for deprecating and removing public API's, is changing the behavior of existing API's also acceptable?
Thanks :)
It used to work on certain cases, but not in others. I don't remember the issue now, but constructor mocking was changed to be more robust, at the price of breaking some existing tests.
As always, the recommendation to avoid such problems is to do less mocking. The most common mistake people make in using mocking libraries (any one of them) is to use them too much. I learned this after many years of experience with thousands of tests.