I would like to write unit tests for a class which creates an instance of a problematic class, and I am working under the following constraints:
- My class calls some third-party library initialization code.
- The third-party code creates an instance of the problematic class. This is fine, and I should not replace this instance with a mock, in order not to alter the behavior of the third-party library.
- My class creates an instance of the problematic class. I should mock the object construction; for example, I would make the problematic object constructor throw an exception.
I am using Mockito 5.5.0 and JUnit Jupiter 5.9.3.
Here is a fairly minimal example:
class Problematic {
public Problematic(String description) {
System.out.println("Problematic object constructed: " + description);
}
}
class ThirdPartyLibrary {
public static void thirdPartyCode() {
new Problematic("this is fine");
}
}
class MyClass {
public static void myCode() {
ThirdPartyLibrary.thirdPartyCode();
new Problematic("this needs to be mocked");
}
}
class MockProblematicObjectConstructionTest {
@Test
void test() {
try (MockedConstruction mockedProblematic = mockConstruction(Problematic.class, (mock, context) -> {
// Now what?
System.out.println("Context arguments: " + context.arguments());
})) {
MyClass.myCode();
}
}
}
How can I intercept and/or mock Problematic's constructor, but only when it has been called from MyClass?
This is part of an incremental refactoring of legacy code, and I would like to write "pin-down tests" before starting the actual refactoring.
Some refactorings are less likely than others to cause problems. In this case I would suggest to refactor the class so that the construction of your problematic class can be controlled.
It should be trivial to verify that this does not change the behavior of your class (you could start your application once and verify that it still works – or you trust that such a minimal change does not cause problems).
Then in your test, you can change the construction to something else:
Eventually, you can then transition your class to use dependency injection via its constructor and hide/encapsulate the field again.