As we have seen, the JMockit Mockups API provides methods that can be used to
apply mock classes and to stub out classes.
There are times when we want to mock or stub out classes for the entire scope of a test class (that is, for all of
its test methods), or even for the entire scope of a test suite (that is, for all of its test classes).
In test code, the mockit.UsingMocksAndStubs annotation provides an easy way to do just
that, by letting us specify which mock classes to apply and/or which real classes to stub out for a given test class
or test suite class (when using JUnit 4).
It is also possible to specify mock classes and/or classes to be stubbed out for an entire test run through external
configuration, by defining certain system properties via JVM configuration or in a jmockit.properties
file.
@UsingMocksAndStubs
The @UsingMocksAndStubs annotation is applied to a test class with a list
containing one or more MockUp<T> subclasses and/or production classes.
When any test in this annotated test class is executed, those mocks and stubs will be in effect.
This ability is particularly useful to prevent code under test from executing some other code which is not relevant for the test but could interfere with it, take too long to execute, or generate some undesirable output. The following example causes two dependency classes to be fully stubbed out, including any static initialization blocks they may have.
public class UnitUnderTest
{
private final Dependency dep1 = new Dependency(...);
public void doSomething()
{
...
AnotherDependency.staticMethod();
...
}
}
@UsingMocksAndStubs({Dependency.class, AnotherDependency.class})
public final class UnitTest
{
@Test
public void someTestMethod()
{
new UnitUnderTest().doSomething();
}
}
For a JUnit 4 test suite, we can apply a given mock class (and/or stub out production classes) by annotating the test suite class, as the next example shows.
@RunWith(Suite.class)
@Suite.SuiteClasses({MyFirstTest.class, MySecondTest.class})
@UsingMocksAndStubs({JDKLoggingMocks.class, Log4jMocks.class})
public final class TestSuite
{}
In this example, we apply the JDKLoggingMocks and Log4jMocks mock
classes, both available in the mockit.integration.logging package.
With this, any use of the standard java.util.logging API in any test belonging to the specified test
suite will be mocked/stubbed out so that no actual logging output is produced.
Any calls in production code like Logger.getLogger(ProductionClass.class.getName()) will result in a
mock logger object. The same will happen to uses of the Log4J API.
The implementation of the mockit.integration.logging.Log4jMocks class makes for some
interesting and complex use of the state-based mocking/stubbing API, so we reproduce it here for reference (minus the
Javadoc documentation).
import org.apache.log4j.*;
import org.apache.log4j.spi.*;
import mockit.*;
public final class Log4jMocks extends MockUp<Logger>
{
private static final Logger MOCK_LOGGER = new RootLogger(Level.OFF);
Log4jMocks() { new Hierarchy(MOCK_LOGGER).setThreshold(Level.OFF); }
@Mock public static Logger getLogger(String name) { return MOCK_LOGGER; }
@Mock public static Logger getLogger(Class<?> clazz) { return MOCK_LOGGER; }
@Mock public static Logger getRootLogger() { return MOCK_LOGGER; }
@Mock public static Logger getLogger(String name, LoggerFactory lf) { return MOCK_LOGGER; }
@Mock public static void trace(Object message) {}
@Mock public static void trace(Object message, Throwable t) {}
@Mock public static boolean isTraceEnabled() { return false; }
}
The Log4J API provides several static methods to create loggers, so we have a corresponding mock method for each one
of them, which simply returns a global mock instance.
The default constructor of the mock class is called exactly once by JMockit, when the
@UsingMocksAndStubs annotation is applied to the test suite.
We use it here to configure the unmocked parts of the logging system so that it never actually produces any output.
Two special system properties are considered at startup time: jmockit-mocks and
jmockit-stubs.
The first one may contain a comma-separated list of fully qualified class names.
Any such class will be automatically set up as a mock class, provided it extends the
MockUp<T> base class.
The mocks defined in startup mock classes will remain in effect until the end of the test run, for all test classes.
The jmockit-stubs property, similarly, may also contain a comma-separated list of fully qualified class
names.
These are, however, arbitrary classes to be fully stubbed out for the duration of the test run.
If only some methods or constructors need to be stubbed out, they can be specified by appending one or more filters
after the class name, separated from it by a "#" character.
Multiple filters for the same class must be separated by a "|" character.
For methods, the filter should be a regular expression for method names, optionally followed by a comma-separated
list of parameter types between parentheses; for constructors, a filter should only contain the parameter types.
Note that system properties can be passed to the JVM through the standard "-D" command line parameter.
Ant/Maven/etc. build scripts have their own ways of specifying system properties, so check their documentation for
details.
jmockit.properties file
The system properties described above can also be specified in a separate
jmockit.properties file, which should be present at the root of the classpath.
If there are multiple such files in the classpath (either inside jars or plain directories), the values specified
for the same system property are added together.
This allows the creation of reusable mock classes which can be packaged in a jar file with its own properties file;
when added to the execution classpath of a test suite, the mock classes will be automatically applied at startup.
For convenience, JMockit system properties can be specified in the properties file without the
"jmockit-" prefix.