Application-Modernization with JMockit and JUnit
When working with legacy-code it is often a good strategy to start increasing test-coverage. But writing tests without changing the legacy-code seems difficult to achieve in the most cases. Often it is enough to execute internal modules in isolation but sometimes there is not enough modularity for the testcase you’re trying to write. In these cases one is only left with one option: changing the behavior of JDK-provided functionality.
Often considered an anti-pattern it can be very useful when increasing test-coverage on legacy-code, like in the following example.
Increasing test-coverage without changing the code-base
With JMockit and its internal RedefinitionEngine even implementations with keyword “final” can be changed. So this is a little bit like hacking the JDK because as the keyword indicates these implementations were meant to be final meaning they shouldn’t be changeable.
[java title="IntegrationTest.java"] import java.io.IOException; import junit.framework.Assert; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class IntegrationTest { @Test public void shouldMockProcessBuilder() throws IOException { // Mocking for final class ProcessBuilder providing dummy Process final Process processMocked = new ProcessBuilder("java", "-version").start(); new MockUp() { @Mock public Process start() throws IOException { return processMocked; } }; ProcessBuilder processBuilder = new ProcessBuilder(); Process process = processBuilder.start(); Assert.assertEquals(processMocked, process); } } [/java]
What we are doing in the integration-test is basically setting up JMockit and providing a mock-object for JDKs ProcessBuilder implementation where we are “overwriting” the “start”-method.
High-level understanding of JMockits type-redefinition engine
Internally JMockit uses java.lang.Instrumentation to instrument Java programming language code. In contrast to original instrumentation designed by the JDK, JMockit instrumentation is not only the addition of byte-code to methods since it also can change application state or behavior.
JMockit uses internal classes like BaseTypeRedefinition which actually rewrite byte-code by calling ClassDefinition which is finally used for redefining the class.