JMockit An automated testing toolkit for Java

Faking

  1. Mock methods and mock classes
  2. Setting up mocks for a test
    1. Kinds of methods which can have mocks
    2. In-line mock classes
  3. Mocking interfaces
  4. Mocking unspecified implementation classes
  5. Invocation count constraints
  6. Mocking class initializers
  7. Accessing the invocation context
  8. Proceeding into the real implementation
  9. Reusing mocks between tests
    1. Using before/after methods
    2. Reusable mock classes
  10. Mock-ups at the test class/suite level

In the JMockit toolkit, the Mockups API provides support for the creation of fake implementations, or "mock-ups". Fakes are different from the mocking API in that, rather than specifying in a test the invocations we expect a dependency will receive when used by code under test, we modify the implementation of the dependency so that it suits the needs of the test. Typically, a mock-up targets a few methods and/or constructors in the class to be faked, while leaving most other methods and constructors unmodified.

Fake implementations can be particularly useful in integration tests which depend on external entities or resources such as the network, the file system, etc. The faking of dependencies which access such external entities allows us to run the same integration test in two "modes": 1) a "real" mode, where all code (tested code and its dependencies) is exercised normally; and 2) an "emulated" mode, where the "problematic" dependencies have their implementations replaced with fake ones, so that the test can succeed even with no network connection, no file system, or whatever external dependency it would need otherwise. The replacement of real implementations with fake ones is completely transparent to the code which uses those dependencies, and can be switched on and off for different test runs.

For the remaining of this chapter, lets say we want to write tests for an application that uses the javax.security.auth.login.LoginContext class (from the standard JAAS API) in order to authenticate users. In this case, we don't want our tests to actually execute any of the real JAAS code, since it may depend on external configuration and might not easily work in a developer testing environment. Therefore, an application class depending on LoginContext will be the "code under test", while the LoginContext class (the dependency) will have at least some of its methods and constructors "mocked" for any given test which involves authentication logic.

Mock methods and mock classes

In the context of the Mockups API, a mock method is any method in a mock class that gets annotated with @Mock. For short, in this chapter we refer to annotated mock methods simply as "mocks"; in other contexts, the word "mock" may refer to an instance of a mocked class instead. A mock class is any class extending the mockit.MockUp<T> generic base class, where <T> is the type to be mocked. The example below shows several mocks defined in a mock class for our example "real" class, javax.security.auth.login.LoginContext.

public final class MockLoginContext extends MockUp<LoginContext>
{
   @Mock
   public void $init(String name, CallbackHandler callbackHandler)
   {
      assertEquals("test", name);
      assertNotNull(callbackHandler);
   }

   @Mock
   public void login() {}

   @Mock
   public Subject getSubject() { return null; }
}

When a mock class is applied to a real class, the latter gets the implementation of those methods and constructors which have corresponding mocks temporarily replaced with the implementations of the matching mock methods, as defined in the mock class. In other words, the real class becomes "mocked" for the duration of the test which applied the mock class, and will respond accordingly whenever it receives invocations during test execution. At runtime, what really happens is that the execution of a mocked method/constructor is intercepted and redirected to the corresponding mock method, which then executes and returns (unless an exception/error is thrown) to the original caller, without this one noticing that a different method was actually executed. Normally, the "caller" class is the one under test, while the mocked class is a dependency.

Mock classes are often defined as nested (static), inner (non-static), or even more often as anonymous classes inside a JUnit/TestNG test class. There is nothing preventing mock classes from being top-level, though. That would be useful if the mock class is to be reused in multiple test classes. As we'll see later, often the most convenient way to implement mock classes is by making them anonymous and local to an individual test method.

A new mock class is created when we have a "real class" defined in production code which needs to be mocked for a given test. It should define at least one mock method, with any number of additional methods and constructors; it can also define any number of fields.

Each @Mock method must have a corresponding "real method/constructor" with the same signature in the targeted real class. For a mocked method, the signature consists of the method name and parameters; for a mocked constructor, it's just the parameters, with the mock method having the special name "$init". If a matching real method/constructor cannot be found for a given mock method, either in the specified real class or in its super-classes (excluding java.lang.Object), an IllegalArgumentException is thrown when the test attempts to apply the mock class. Notice this exception can be caused by a refactoring in the real class (such as renaming the real method), so it's important to understand why it happens.

Finally, notice there is no need to have mocks for all methods and constructors in a real class. Any such method or constructor for which no corresponding mock exists in the mock class will simply stay "as is", that is, it won't be mocked. This, of course, assuming there isn't some other mock class applied by the same test to the same real class, something which is perfectly valid (and sometimes useful). If two (or more) mock classes happen to be applied to the same real class for the same test, then such mock classes should avoid defining the same mock twice. In case a mock is duplicated, though, the "last" one to be applied wins.

Setting up mocks for a test

A given mock class must be applied to a corresponding real class to have any effect. We call this "setting up" the mock class. This is usually done inside an individual test method or inside a @BeforeMethod (TestNG) or @Before (JUnit 4) method. Once a mock class is set up, all executions of the mocked methods and constructors of the real class get automatically redirected to the corresponding mock methods.

To set up the MockLoginContext mock class above, we simply instantiate it:

@Test
public void settingUpAMockClass() throws Exception
{
   new MockLoginContext());

   // Inside an application class which creates a suitable CallbackHandler:
   new LoginContext("test", callbackHandler).login();

   ...
}

Since the mock class is set up inside a test method, the mocking of LoginContext by MockLoginContext will be in effect only for that particular test.

When the constructor invocation that instantiates LoginContext executes, the corresponding "$init" mock method in MockLoginContext will be executed, asserting the validity of the invocation arguments. Similarly, when the LoginContext#login method is called, the corresponding mock method will be executed, which in this case will do nothing since the method has no parameters and void return type. The mock class instance on which these invocations occur is the one created in the first part of the test.

The (partial) example test above simply verifies that the LoginContext class is instantiated with valid arguments through a particular constructor that takes a context name and a callback handler. If the real class is not instantiated at all, the test would still pass (unless some other condition causes it to fail). The invocation of the login method also has no effect on the outcome of this test, except for the fact that such an invocation will result in the execution of an empty mock method instead of the real method.

Now, what if we wanted to simulate an authentication failure for a different test? The LoginContext#login() method declares that it can throw a LoginException "if the authentication fails", so what we need to do is very simple (using JUnit 4 in this example):

public static class MockLoginContextThatFailsAuthentication extends MockUp<LoginContext>
{
   @Mock
   public void $init(String name) {}

   @Mock
   public void login() throws LoginException
   {
      throw new LoginException();
   }
}

@Test(expected = LoginException.class)
public void settingUpAnotherMockClass() throws Exception
{
   new MockLoginContextThatFailsAuthentication();

   // Inside an application class:
   new LoginContext("test").login();
}

This test will only pass if the LoginContext#login() method throws an exception, which it will when the corresponding mock method is executed.

Kinds of methods which can have mocks

So far, we have only mocked public instance methods with public instance mock methods. In reality, any other kind of method in a real class can be mocked: methods with private, protected or "package-private" accessibility, static methods, final methods, and native methods. (Also synchronized and strictfp methods, but these modifiers only affect the method implementation, not its interface.) Even more, a static method in the real class can be mocked by an instance mock method, and vice-versa (an instance real method with a static mock); the same applies for the final modifier.

Methods to be mocked need to have an implementation, though not necessarily in bytecode (in the case of native methods). Therefore, an abstract method cannot be mocked directly, and the same applies to the methods of a Java interface. (That said, as shown later the Mockups API can automatically create a proxy class that implements an interface.)

In-line mock classes

Typically, a specific group of mock methods for a given real class will only be useful for a single test. In such a situation we can create an anonymous mock class inside an individual test method, as demonstrated by the next example.

@Test
public void settingUpMocksUsingAnAnonymousMockClass() throws Exception
{
   new MockUp<LoginContext>() {
      @Mock void $init(String name) { assertEquals("test", name); }
      @Mock void login() {}
   });

   new LoginContext("test").login();
}

Note that mock methods don't need to be public.

Mocking interfaces

Most of the time a mock class targets a real class directly. But what if we need a mock object that implements a certain interface, to be passed to code under test? The following example test shows how it is done for the interface javax.security.auth.callback.CallbackHandler.

@Test
public void mockingAnInterface() throws Exception
{
   CallbackHandler callbackHandler = new MockUp<CallbackHandler>() {
      @Mock
      void handle(Callback[] callbacks)
      {
         assertEquals(1, callbacks.length);
         assertTrue(callbacks[0] instanceof NameCallback);
      }
   }.getMockInstance();

   callbackHandler.handle(new Callback[] {new NameCallback("Enter name:")});
}

The MockUp#getMockInstance() method returns a proxy object that implements the desired interface.

Mocking unspecified implementation classes

To demonstrate this feature, lets consider the following code under test.

public interface Service { int doSomething(); }
final class ServiceImpl implements Service { public int doSomething() { return 1; } }

public final class TestedUnit
{
   private final Service service1 = new ServiceImpl();
   private final Service service2 = new Service() { public int doSomething() { return 2; } };

   public int businessOperation()
   {
      return service1.doSomething() + service2.doSomething();
   }
}

The method we want to test, businessOperation(), uses classes that implement a separate interface, Service. One of these implementations is defined through an anonymous inner class, which is completely inaccessible (except for the use of Reflection) from client code.

Given a base type (be it an interface, an abstract class, or any sort of base class), we can write a test which only knows about the base type but where all implementing/extending implementation classes get mocked. To do so, we create a mock-up whose mocked type refers only to the known base type, and does so through a type variable. Not only will implementation classes already loaded by the JVM get mocked, but also any additional classes that happen to get loaded by the JVM during later test execution. This ability is demonstrated below.

@Test
public <T extends Service> void mockingImplementationClassesFromAGivenBaseType()
{
   new MockUp<T>() {
      @Mock int doSomething() { return 7; }
   };

   int result = new TestedUnit().businessOperation();

   assertEquals(14, result);
}

In the test above, all invocations to methods implementing Service#doSomething() will be redirected to the mock method implementation, regardless of the actual class implementing the interface method.

Invocation count constraints

All example tests shown so far only used JUnit/TestNG "asserts" to verify invocation arguments. Sometimes, though, we may want to verify if a given method/constructor in a dependency is invoked at all by the code under test. We may also want to verify exactly how many invocations a given mock received during the execution of a test, or specify that the test should fail if more/less than a certain number of invocations occurs. For this, we can specify declarative constraints on the invocation count of a given mock, as the following example shows.

@Test
public void specifyingInvocationCountConstraints() throws Exception
{
   new MockUp<LoginContext>() {
      @Mock(minInvocations = 1)
      void $init(String name) { assertEquals("test", name); }

      @Mock(invocations = 1)
      void login() {}

      @Mock(maxInvocations = 1)
      void logout() {}
   });

   new LoginContext("test").login();
}

In this test we used all three attributes of the @Mock annotation related to invocation counts. The first mock specifies that the LoginContext(String) constructor must be invoked at least once during the test. The second one specifies that the login() method must be invoked exactly once, while the third declares that logout() can be invoked, but not more than once.

It is also valid to specify both minInvocations and maxInvocations on the same mock, in order to constrain the invocation count to a given range.

Mocking class initializers

When a class in production code performs some work in one or more static initialization blocks, we may need to mock it out so it doesn't interfere with test execution. We can define a special mock method for that, as shown below.

@Test
public void mockingStaticInitializers()
{
   new MockUp<ClassWithStaticInitializers>() {
      @Mock
      void $clinit()
      {
         // Do nothing here (usually).
      }
   };

   ClassWithStaticInitializers.doSomething();
}

Special care must be taken when the static initialization code of a class is mocked out. Note that this includes not only any "static" blocks in the class, but also any assignments to static fields (excluding those resolved at compile time, which do not produce executable bytecode). Since the JVM only attempts to initialize a class once, restoring the static initialization code of a mocked out class will have no effect. So, if you mock out the static initialization of a class that hasn't been initialized by the JVM yet, the original class initialization code will never be executed in the test run. This will cause any static fields that are assigned with expressions computed at runtime to instead remain initialized with the default values for their types.

Accessing the invocation context

A mock method can optionally declare an extra parameter of type mockit.Invocation, provided it is the first parameter. For each actual invocation to the corresponding mocked method/constructor, an Invocation object will be automatically passed in when the mock method is executed.

This invocation context object provides several "getters" which can be used inside the mock method. One is the getInvokedInstance() method, which returns the mocked instance on which the invocation occurred (null if the mocked method is static). Other getters provide the number of invocations (including the current one) to the mocked method/constructor, the invocation count constraints (if any) as specified in the @Mock annotation, etc. Below we have an example test.

@Test
public void accessingTheMockedInstanceInMockMethods() throws Exception
{
   final Subject testSubject = new Subject();

   new MockUp<LoginContext>() {
      @Mock
      void $init(Invocation invocation, String name, Subject subject)
      {
         assertNotNull(name);
         assertSame(testSubject, subject);

         // Gets the invoked instance.
         LoginContext loginContext = invocation.getInvokedInstance();

         // Verifies that this is the first invocation.
         assertEquals(1, invocation.getInvocationCount());

         // Forces setting of private Subject field, since no setter is available.
         Deencapsulation.setField(loginContext, subject);
      }

      @Mock(minInvocations = 1)
      void login(Invocation invocation)
      {
         // Gets the invoked instance.
         LoginContext loginContext = invocation.getInvokedInstance();

         // getSubject() returns null until the subject is authenticated.
         assertNull(loginContext.getSubject());

         // Private field set to true when login succeeds.
         Deencapsulation.setField(loginContext, "loginSucceeded", true);
      }

      @Mock
      void logout(Invocation invocation)
      {
         // Gets the invoked instance.
         LoginContext loginContext = invocation.getInvokedInstance();

         assertSame(testSubject, loginContext.getSubject());
      }
   };

   LoginContext theMockedInstance = new LoginContext("test", testSubject);
   theMockedInstance.login();
   theMockedInstance.logout();
}

Proceeding into the real implementation

Once a @Mock method is executing, any additional calls to the corresponding mocked method are also redirected to the mock method, causing its implementation to be re-entered. If, however, we want to execute the real implementation of the mocked method, we can call the proceed() method on the Invocation object received as the first parameter to the mock method.

The example test below exercises a LoginContext object created normally (without any mocking in effect at creation time), using an unspecified configuration. (For the complete version of the test, see the mockit.MockAnnotationsTest class.)

@Test
public void proceedIntoRealImplementationsOfMockedMethods() throws Exception
{
   // Create objects to be exercised by the code under test:
   LoginContext loginContext = new LoginContext("test", null, null, configuration);

   // Set up mocks:
   ProceedingMockLoginContext mockInstance = new ProceedingMockLoginContext();

   // Exercise the code under test:
   assertNull(loginContext.getSubject());
   loginContext.login();
   assertNotNull(loginContext.getSubject());
   assertTrue(mockInstance.loggedIn);

   mockInstance.ignoreLogout = true;
   loginContext.logout(); // first entry: do nothing
   assertTrue(mockInstance.loggedIn);

   mockInstance.ignoreLogout = false;
   loginContext.logout(); // second entry: execute real implementation
   assertFalse(mockInstance.loggedIn);
}

static final class ProceedingMockLoginContext extends MockUp<LoginContext>
{
   boolean ignoreLogout;
   boolean loggedIn;

   @Mock
   void login(Invocation inv) throws LoginException
   {
      try {
         inv.proceed(); // executes the real code of the mocked method
         loggedIn = true;
      }
      finally {
         // This is here just to show that arbitrary actions can be taken inside
         // the mock, before and/or after the real method gets executed.
         LoginContext lc = inv.getInvokedInstance();
         System.out.println("Login attempted for " + lc.getSubject());
      }
   }

   @Mock
   void logout(Invocation inv) throws LoginException
   {
      // We can choose to proceed into the real implementation or not.
      if (!ignoreLogout) {
         inv.proceed();
         loggedIn = false;
      }
   }
}

In the example above, all the code inside the tested LoginContext class will get executed, even though some methods (login and logout) are mocked. This example is contrived; in practice, the ability to proceed into real implementations would not normally be useful for testing per se, not directly at least.

You may have noticed that use of Invocation#proceed(...) in a mock method effectively behaves like advice (from AOP jargon) for the corresponding real method. This is a powerful ability that can be useful for certain things (think of an interceptor or decorator).

For more details on all the methods available in the mockit.Invocation class, see its API documentation.

Reusing mocks between tests

Most tests will probably only use dedicated mock classes, specifically created for each particular test. There will be times, though, when the same mock class can be reused by multiple test methods, either in a single test class or across the entire test suite. We will now see different ways to set up mocks so they are shared by a whole group of tests, as well as ways to define reusable mock classes.

Using before/after methods

In a given test class, we can define instance methods that will run before and after each test method (even when the test throws an error or exception). With JUnit, we use the @Before and @After annotations on one or more arbitrary instance methods of the test class. The same applies to the @BeforeMethod and @AfterMethod annotations when using TestNG.

Any mock class that can be applied from inside a test method can also be applied from a "before" method, by instantiating a mock-up class. Such a mock class will remain in effect for the execution of all test methods in the test class. The only difference of applying mocks in a "before" method is that they also remain in effect inside "after" methods, if any.

For example, if we wanted to mock the LoginContext class with a mock-up class for a bunch of related tests, we would have the following methods in the test class:

public class MyTestClass
{
   @Before
   public void setUpSharedMocks()
   {
      new MockUp<LoginContext>() {
         // shared mocks here...
      };
   }

   // test methods that will share the mocks set up above...
}

The example above uses JUnit, but the equivalent code for TestNG would be practically the same.

It is also valid to extend from base test classes, which may optionally define "before" and/or "after" methods containing calls to the Mockups API.

Reusable mock classes

Named mock classes can be designed as concrete (and optionally final) classes that are then used in specific tests. When instantiated directly by test code, such mock instances can be configured through constructor arguments, fields, or non-mock methods. Alternatively, they can be designed as base classes (possibly abstract) to be extended by concrete mock classes inside specific test classes or methods.

The example tests for this section come from JMockit's own test suite. They exercise the following class, partially reproduced here:

public final class TextFile
{
   // fields and constructors that accept a TextReader or DefaultTextReader object...
   
   public List<String[]> parse()
   {
      skipHeader();

      List<String[]> result = new ArrayList<String[]>();

      while(true) {
         String  strLine = nextLine();

         if (strLine == null) {
            closeReader();
            break;
         }

         String[] parsedLine = strLine.split(",");
         result.add(parsedLine);
      }

      return result;
   }

   // private helper methods that call "skip(n)", "readLine()", and "close()"...
   
   public interface TextReader
   {
      long skip(long n) throws IOException;
      String readLine() throws IOException;
      void close() throws IOException;
   }

   static final class DefaultTextReader implements TextReader
   {
      DefaultTextReader(String fileName) throws FileNotFoundException { ...mocked... }
      public long skip(long n) throws IOException { ...mocked... }
      public String readLine() throws IOException { ...mocked... }
      public void close() throws IOException { ...mocked... }
   }
}

Some of the tests for the class above are as follows.

public final class TextFileUsingMockUpsTest
{
   // A reusable mock-up class to be applied in specific tests.
   static final class MockTextReaderConstructor extends MockUp<DefaultTextReader>
   {
      @Mock(invocations = 1)
      void $init(String fileName) { assertThat(fileName, equalTo("file")); }
   }

   @Test
   public void parseTextFileUsingDefaultTextReader() throws Exception
   {
      new MockTextReaderConstructor();
      new MockTextReaderForParse<DefaultTextReader>() {};

      List<String[]> result = new TextFile("file", 200).parse();

      // assert result from parsing
   }

   ...

The test above uses two reusable mock classes. The first one encapsulates a mock for the single constructor of the TextFile.DefaultTextReader nested class. Any tests exercising code in the TextFile class that invokes this constructor will therefore use this mock class. It is applied by simply being instantiated inside the test method.

The second mock class used by the test targets the same DefaultTextReader class. As we see next, it defines mocks for a whole different set of members, which happen to be the methods called from the TextFile#parse() method.

...

// A reusable base mock class to be extended in specific tests.
static class MockTextReaderForParse<T extends TextReader> extends MockUp<T>
{
   static final String[] LINES = { "line1", "another,line", null};
   int invocation;

   @Mock(invocations = 1)
   long skip(long n)
   {
      assertEquals(200, n);
      return n;
   }

   @Mock(invocations = 3)
   String readLine() throws IOException { return LINES[invocation++]; }

   @Mock(invocations = 1)
   void close() {}
}

...

The mock-up class above, like the mockit.MockUp<T> class which it extends, is generic. In this particular case, this is necessary because the tested TextFile class works with two different types for the "text reader" dependency: TextFile.TextReader (an interface which client code can implement), and TextFile.DefaultTextReader (an internal default implementation of the interface). The previous test simply used this mock class as is, by defining an anonymous subclass which specifies the type to be mocked as the DefaultTextReader concrete class. The next test, on the other hand, passes a TextReader implementation to TextFile:

...

@Test
public void parseTextFileUsingProvidedTextReader() throws Exception
{
   TextReader textReader = new MockTextReaderForParse<TextReader>() {}.getMockInstance();

   List<String[]> result = new TextFile(textReader, 200).parse();

   // assert result from parsing
}

...

The interface implementation, in this case, is a mock proxy object obtained through the MockUp<T>#getMockInstance() method.

Finally, we get to a more interesting case, where the concrete mock subclass actually overrides some of the mocks inherited from the base mock class:

...

@Test
public void doesNotCloseTextReaderInCaseOfIOFailure() throws Exception
{
   new MockTextReaderConstructor();

   new MockTextReaderForParse<DefaultTextReader>() {
      @Override @Mock
      String readLine() throws IOException { throw new IOException(); }

      @Override @Mock(invocations = 0)
      void close() {}
   };

   TextFile textFile = new TextFile("file", 200);

   try {
      textFile.parse();
      fail();
   }
   catch (RuntimeException e) {
      assertTrue(e.getCause() instanceof IOException);
   }
}

The test forces an IOException to be thrown in the first call to readLine(). (This exception will get wrapped in a RuntimeException by the parse method.) It also specifies, through an invocation count constraint, that the close() method should never be called. This shows that not only the behavior of the inherited mock is overridden, but also any of the metadata specified through the @Mock annotation.

Mock-ups at the test class/suite level

As we have seen, mock-up classes are usually applied for the duration of an individual test. There may be times when we want to mock classes for the entire scope of a test class (that is, for all of its test methods), or for the entire scope of a test suite (that is, for all of its test classes). It is also possible to specify mock classes for an entire test run through external configuration, by setting a JVM-wide system property or adding a jmockit.properties file to the runtime classpath.

Programmatic application of mock-ups in wider scopes

To have a mock-up class cover the entire scope (all tests) of a given test class, we simply apply it inside a @BeforeClass method (with JUnit or TestNG). To apply mock-ups over a test suite, we can use a TestNG @BeforeSuite method, or a JUnit Suite class. The next example shows a JUnit 4 test suite configuration with the application of mock-ups.

@RunWith(Suite.class)
@Suite.SuiteClasses({MyFirstTest.class, MySecondTest.class})
public final class TestSuite
{
   @BeforeClass
   public static void setupMocks()
   {
      new LoggingMocks();

      new MockUp<SomeClass>() {
         @Mock someMethod() {}
      };
   }
}

In this example, we apply the LoggingMocks mock class and an inline mock-up class; their mocks will be in effect until just after the last test in the test suite has been executed.

External application through a system property

The jmockit-mocks system property supports a comma-separated list of fully qualified mock-up class names. If specified at JVM startup time, any such class (which must extend MockUp<T>) will be automatically applied for the whole test run. The mocks defined in startup mock classes will remain in effect until the end of the test run, for all test classes.

Note that a system property 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.

External application through the jmockit.properties file

Mock-up classes specified in the jmockit-mocks system property can alternatively 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), all class names listed 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, the entry in the properties file can be written without the "jmockit-" prefix.