State-based testing with JMockit

  1. Mock methods and mock classes
  2. Setting up mocks for a test
    1. Setting up mocks for a real class chosen at setup time
    2. Which kinds of methods can have mocks?
    3. Using the @MockClass annotation
    4. In-line mock classes
  3. Mocking interfaces
  4. Stubbing out classes and methods
    1. Using the stubbing methods
    2. Specifying stubs with a mock class
  5. Invocation count constraints
  6. Mocking and stubbing class initializers
  7. Controlling the instantiation of mock classes
    1. Instantiation per Mock Setup
    2. Instantiation per Mock Invocation
    3. Instantiation per Mocked Instance
  8. Accessing the mocked object: the "it" field
  9. Reentrant mocks
  10. Reusing mocks between tests
    1. Using before/after methods
    2. Reusable mock classes

In the JMockit toolkit, the Mockups API provides support for the creation of state-based tests and for the creation of fake implementations. In this kind of testing, the focus is not on the interactions between a tested class and its dependencies, but on the data received by and returned from those dependencies; rather than writing a test from the perspective of the tested class, we write it from the perspective of the used classes, regardless of who is using them. As for fake implementations, they can be particularly useful in integration tests, where tested classes with dependencies on external entities (the network, the file system, etc.) can be transparently isolated from the implementations of those "problematic" dependencies.

Regular state verification, performed after exercising the unit under test, is typically done through JUnit/TestNG assertions written at the end of each test method. The Mockups API extends the ability of a test to do state verification, by allowing those same assertions to be executed whenever a dependency is invoked from inside the tested code. As the tested unit passes data to dependencies through method and constructor invocations, the corresponding mock implementations specified for those dependencies get called. These implementations (usually defined inside test classes) will then execute the appropriate assertions on the received invocation arguments. In addition, the API allows the test to verify if any given method or constructor of a mocked dependency is called at all, or more generally to verify the actual number of invocations against an specified exact, minimum, or maximum number of invocations.

For the remaining of this chapter, lets say we want to write unit 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. Besides, we also don't want to exercise real JAAS code in a unit test because obviously that is not part of the application code we are trying to test. (Of course, in integration tests we just might work with a real JAAS setup.) Therefore, an application class depending on LoginContext will be our "unit 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 the mockit.Mock annotation. A mock class is any class that is annotated with mockit.MockClass, that extends the mockit.MockUp<T> generic base class, or that is explicitly passed to one of several mock-setup methods. For short, in this chapter we will refer to such annotated mock methods simply as "mocks" (in other contexts, the word "mock" may refer to an instance of a mocked class instead). The example below shows several mocks defined in a named mock class.

   public static class MockLoginContext
   {
      @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/redefined 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 classes inside a JUnit/TestNG test class. That's why the example mock class above is declared to be static; it could also have been an inner (non-static) 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, though, 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 normally done for a specific test or set of tests. In the first case, it occurs inside individual test methods. In the second one, a @BeforeMethod (TestNG), setUp() (in JUnit 3.8-style tests), or @Before (JUnit 4) method can be used. During the time a mock class is in effect, the methods and constructors of the real class which have corresponding mocks will stay mocked, with any calls made on them automatically and transparently redirected to the corresponding mocks for execution. There are a few different ways to set up mock classes, each with a different way to specify the corresponding real class, as we'll now see.

Setting up mocks for a real class chosen at setup time

For the MockLoginContext mock class above, the corresponding real class is javax.security.auth.login.LoginContext. To set it up for an individual test, we could write the following (assuming the necessary imports):

   @Test
   public void setUpMocksForGivenRealClassWithGivenMockInstance() throws Exception
   {
      Mockit.setUpMock(LoginContext.class, new MockLoginContext());

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

      ...
   }

Since the call above to Mockit.setUpMock occurs inside a test method, the mocking of LoginContext by MockLoginContext will be in effect only for this 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.

Notice the test instantiates the mock class and passes this instance in the call to Mockit.setUpMock(Class realClass, Object mock). This mock instance will be the one on which any mock method (such as login) is called.

The (partial) example test above would simply verify that the LoginContext class is instantiated with valid arguments when one particular constructor that takes a context name and a callback handler is used. If the 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
   {
      @Mock
      public void $init(String name) {}

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

   @Test(expected = LoginException.class)
   public void setUpMocksForGivenRealClassWithoutPrecreatedMockInstance() throws Exception
   {
      Mockit.setUpMock(LoginContext.class, MockLoginContextThatFailsAuthentication.class);

      // 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. Notice that in this case we passed the class literal for the mock class instead of an instance. This will cause a new instance of the mock class to be created automatically every time a non-static mock method needs to be called (which happens whenever a mocked method on the real class is called). The mock method will be called on this new instance, which will then be discarded (that is, it won't be reused later for new invocations). Naturally, mock methods that are static are never called on any instance, so no new instance is created for them.

Which kinds of methods 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, an static method in the real class can be mocked by an instance mock method, and vice-versa (an instance real method with an 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 a named mock class, mocks usually need to be declared as public, otherwise an IllegalAccessError might get thrown.

Using the @MockClass annotation

The Mockit.setUpMock method, in all of its two-parameter variations, requires the real class to be explicitly provided through an argument. The Mockit.setUpMocks method, in contrast, takes only mock class literals and/or mock class instances. The applicable real class is specified through the mockit.MockClass annotation, applied on the mock class itself, as the following example shows.

   @MockClass(realClass = LoginContext.class)
   public static class MockLoginContext
   {
      // @Mocks here...
   }

   @MockClass(realClass = SomethingElse.class)
   public final class MockSomethingElse
   {
      // @Mocks here...
   }

   @Test
   public void setUpMocksForMultipleClasses()
   {
      Mockit.setUpMocks(MockLoginContext.class, new MockSomethingElse());

      // Calls code under test that uses the "LoginContext" and "SomethingElse" classes:
      ...
   }

The realClass attribute is mandatory for @MockClass. A mock class is typically used for the same real class every time. Therefore, nothing more natural than associating said class directly with the mock class, avoiding the need to specify it every time the mocks are set up.

In-line mock classes

Named mock classes, typically defined as nested or inner classes of the test class, definitely have their place. Often, however, an specific group of mocks for a given real class will be useful for a single test. In such a situation we can create anonymous mock classes inside individual test methods, as demonstrated by the next example.

   @Test
   public void setUpMocksUsingAnonymousMockClass() throws Exception
   {
      Mockit.setUpMock(LoginContext.class, new Object() {
         @Mock void $init(String name) { assertEquals("test", name); }
         @Mock void login() {}
      });

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

An anonymous mock class is always instantiated at the place of definition and cannot be annotated with @MockClass, so we need a mock setup mechanism that takes both the real class and the mock instance.

A more elegant alternative to the use of Mockit.setUpMock with an "in-line mock class" is shown next, using the mockit.MockUp<T> generic base class.

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

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

In this case, we still have an anonymous mock class, but it is created as a subclass of MockUp<T> where the real class is specified through the generic type parameter T. (Subclasses of MockUp can also be named, of course.) Given this succinct and readable syntax, and the fact that mock classes tend to be specific to individual tests, the creation of such mock-up classes should be the most common way to define mocks with the Mockups API.

As the previous example tests have shown, it is perfectly safe to declare mock methods in an anonymous mock class as non-public. This is an added bonus over annotated (and named) mock classes, which often (but not always) need to be public with their mock methods also 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 two following example tests show how it is done, one using an annotated mock class, the other a mock-up class. The mocked interface is javax.security.auth.callback.CallbackHandler.

   @Test
   public void setUpMocksForInterface() throws Exception
   {
      CallbackHandler callbackHandler = Mockit.setUpMock(new MockCallbackHandler());

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

   @MockClass(realClass = CallbackHandler.class)
   public static class MockCallbackHandler
   {
      @Mock
      public void handle(Callback[] callbacks)
      {
         assertEquals(1, callbacks.length);
         assertTrue(callbacks[0] instanceof NameCallback);
      }
   }

   @Test
   public void mockInterfaceWithMockUp() 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 Mockit.setUpMock(Object) method returns a proxy object that implements the desired interface, when the mock class specifies that interface as the value of the realClass attribute (if an actual class is specified in the attribute, the method will apply the mock class and return null). The method accepts either an instance of the annotated mock class or the Class literal for the same. Again, a more elegant alternative is obtained with the use of a mock-up class, which provides the proxy instance through a call to the getMockInstance() method. This method returns null if the mocked type is not an interface.

Notice that for the first test the mock class and its mock methods are declared to be public and must be so to avoid an IllegalAccessError, while in the second test the mock-up class and the mock method don't have to be public (an anonymous class is always non-public anyway).

Stubbing out classes and methods

Sometimes all we need is to stub out the executable code in an entire class or in selected methods/constructors. When a method is stubbed out, it does nothing when executed. An stubbed out constructor will also do nothing, except for calling the necessary constructor in the super-class. Methods with non-void return type will simply return the default value for that type: zero for numeric primitive types, false for boolean, an empty array or collection, or null for any other reference type (including String).

Special care must be taken when the static initialization code of a class is stubbed 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 any executable bytecode). Since the JVM only attempts to initialize a class once, restoring the static initialization code of an stubbed out class will not have any effect. So, if you stub 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 (except those assigned with compile-time constants) to retain their initial default values.

Using the stubbing methods

The Mockit class provides a set of "stubbing" methods with names starting in "stubOut". They accept one or more classes to be stubbed out, with some of them also accepting a set of filters for methods and constructors. The static initializer of a class is specified by the special filter value "<clinit>". For details and examples, see the relevant API documentation.

Specifying stubs with a mock class

The @MockClass annotation also gives us the ability to specify methods and/or constructors in the real class that should be stubbed out so they have no effect when called during a test. Stubbing out a method achieves the same result as mocking it with a mock method that does nothing, except for returning the default value according to the method return type when it's not void.

The following example tests make use of this feature.

   @Test
   public void mockAndStubOutClassAccordingToSpecifiedStubs() throws Exception
   {
      Mockit.setUpMocks(new MockLoginContextWithStubs());

      LoginContext context = new LoginContext("");
      context.login();
      context.logout();
   }

   @MockClass(realClass = LoginContext.class, stubs = {"(String)", "logout"})
   final class MockLoginContextWithStubs
   {
      @Mock
      void login() {} // this could also have been an stub
   }

   @Test
   public void mockAndStubOutClassAccordingToSpecifiedInverseStubs() throws Exception
   {
      Mockit.setUpMocks(MockLoginContextWithInverseStubs.class);

      LoginContext context = new LoginContext("", null, null);
      context.login();
      context.logout();
   }

   @MockClass(realClass = LoginContext.class, stubs = "", inverse = true)
   static class MockLoginContextWithInverseStubs
   {
      @Mock
      static void login() {}
   }

In the first test above, the constructor of the real class (LoginContext) which has one parameter of type String is stubbed out, as well as all methods in the same class with name logout. Parameters are ignored if not specified in the stub filter, so all overloads (in case there is more than one) with the same method name will be stubbed out.

The second mock class, MockLoginContextWithInverseStubs, uses the inverse attribute (which by default is false) to specify that all methods and constructors not matching the specified stub filters will be stubbed out. In this example, the list of stub filters contains only an empty filter which matches nothing, so everything in the real class is stubbed out. Any mocks defined in the mock class will override the stub, however.

Each stub filter string specified with the stubs attribute can match any number of methods of the real class, since the initial part actually is a regular expression for method names. For constructors, this part is left empty since they have no names. The second part in a stub filter string, which can be empty for methods but never for constructors, is a comma-separated list of parameter type names, between parentheses. Each parameter type name either is the name of a primitive type, or a fully qualified reference type name. For convenience, it can be a unique suffix for such type names instead; this makes it easy to specify reference type names, since in most cases the package name can be omitted. Finally, as seen in the example above, the empty stub filter will match no methods or constructors in the real class, or all methods and constructors if used with inverse = true.

Invocation count constraints

All example tests shown so far only used JUnit/TestNG "asserts" to verify invocation arguments. This is the part of the API that extends conventional state-based testing to the data items exchanged between objects. Sometimes, though, we may want to verify if a given method/constructor in a dependency is invoked at all by the unit 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 specifyInvocationCountConstraints() throws Exception
   {
      Mockit.setUpMock(LoginContext.class, new Object()
      {
         @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 the test above we used an anonymous mock class with the setUpMock method, although a nicer solution could be obtained with a local MockUp object.) 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 and stubbing class initializers

When a class in production code performs some work in one or more static initialization blocks, we often need to stub it out so it doesn't interfere with test execution. To do just that for a given real class, we can write something like @MockClass(realClass = MyRealClass.class, stubs = "<clinit>"). If we instead want some test code to execute when the static initialization of the real class gets executed by the JVM, then we can define an special mock, as shown below.

   @Test
   public void mockStaticInitializer()
   {
      new MockUp<ClassWithStaticInitializers>()
      {
         @Mock(invocations = 1)
         void $clinit()
         {
            // Do something here.
         }
      };

      ClassWithStaticInitializers.doSomething();
   }

We can even verify that static initialization occurs, as specified by the invocation count constraint above. Notice however that once loaded by the JVM, a class will be initialized no more than once, so the test would fail if it runs after the class has already been initialized.

Controlling the instantiation of mock classes

We have already seen two different situations when mock classes get instantiated:

  1. The mock class is instantiated directly by test code with the new operator, and passed to JMockit in a call to a Mockit.setUpMock or Mockit.setUpMocks method, or alternatively as a MockUp subclass instance. In this case the same mock instance is used for all calls to instance mock methods, as long as the mock class remains in effect.
  2. The mock class is instantiated "behind the covers" by JMockit whenever it needs an instance to invoke an instance mock method on. In this situation a mock instance is created, a mock is executed on it, and then it is discarded (that is, it becomes eligible for garbage collection).

Sometimes it may be important to have more control on when exactly a mock class is automatically instantiated by JMockit. There is also a situation which is not covered by the previously described instantiation mechanisms, and which may be needed or useful for certain test cases: instantiation per mocked instance. To cover such needs the @MockClass annotation has an instantiation attribute, whose values are given by the Instantiation "enum". The default value for this attribute is Instantiation.PerMockInvocation.

The following sub-sections describe each of the possible instantiation modes which can be used to control how and when JMockit creates automatic mock class instances. Notice this is applicable only to situation 2 above.

Instantiation per Mock Setup

For a mock class annotated as @MockClass(realClass = ..., instantiation = PerMockSetup), a call like Mockit.setUpMock(realClass, MyMockClass.class) or Mockit.setUpMocks(MyMockClass.class) will result in the creation of the mock instance at the time of the call. This single mock instance will then be used for all instance method invocations, for as long as the mock class remains in effect.

Notice this is equivalent to passing an instance created by test code in one of those mock setup calls.

Instantiation per Mock Invocation

For a mock class annotated as @MockClass(realClass = ..., instantiation = PerMockInvocation), a call like Mockit.setUpMock(realClass, MyMockClass.class) or Mockit.setUpMocks(MyMockClass.class) will not result in the creation of any mock instance at this time, but will later if and when an instance mock method needs to be invoked. For each such invocation, a new mock instance is created and then discarded as soon as the mock call terminates.

Notice this is the default JMockit behavior when a class literal for the mock class is passed in a mock setup call, and the instantiation attribute isn't specified.

Instantiation per Mocked Instance

For a mock class annotated as @MockClass(realClass = ..., instantiation = PerMockedInstance), a call like Mockit.setUpMock(realClass, MyMockClass.class) or Mockit.setUpMocks(MyMockClass.class) will result in the on-demand creation of one new mock instance for each and every new mocked instance (ie, an instance of the real class). In other words, once the mock class is in effect, whenever a mocked instance method of the real class is invoked, a paired instance of the mock class will be created if none yet exists for that particular instance of the real class; this same mock instance will then be used for all invocations to mock instance methods.

The ability to have a mock class instance for each real class instance will normally only be useful if the mock class stores some state that is specific to each instance of the real class.

Notice the mock object lifetime described above can only be achieved by using this particular instantiation mode.

Accessing the mocked object: the "it" field

As we have seen, there is a relationship between mocked instances (that is, instances of real classes which have been mocked) and mock instances (instances of mock classes). Mock instances can be created explicitly in test code, or automatically by JMockit according to the instantiation mode of the mock class. Mocked instances are created inside tested code, or in test code so they can be passed to tested code.

For certain use cases, a mock method executing on a mock instance may need access to the corresponding mocked instance. This can be achieved by declaring an instance field of name it in the mock class, with the type of the mocked real class. Every time a mocked instance method is redirected to execute the corresponding mock instance method, the it field on the mock instance will be set to the mocked instance. If a mocked constructor has a corresponding $init mock method, then the field will be set to the newly created (and uninitialized) instance.

In practice, the use of an it field by itself is uncommon, so the following example test is a contrived (although interesting) one. The next section describes a more likely use for such fields.

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

      new MockUp<LoginContext>()
      {
         LoginContext it;

         @Mock(invocations = 1)
         void $init(String name, Subject subject)
         {
            assertNotNull(name);
            assertSame(testSubject, subject);

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

         @Mock(invocations = 1)
         void login()
         {
            // getSubject() returns null until the subject is authenticated.
            assertNull(it.getSubject());

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

         @Mock(invocations = 1)
         void logout()
         {
            assertSame(testSubject, it.getSubject());
         }
      };

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

The example above (which is an actual test in JMockit's own test suite) used a mock-up class, but annotated and regular anonymous mock classes can also declare an it field. The setField methods used in this test are statically imported from the mockit.Deencapsulation utility class, and are only used here to make the test more meaningful. The local variable theMockedInstance contains the instance which the it field will be set to on the mock-up instance, for each of the three mock invocations in this particular test.

Reentrant mocks

Usually, a reentrant method is one which can be re-entered once it is already executing. Recursive methods, for example, are typically reentrant. A reentrant mock method is similar, but instead of causing itself to be re-entered, it causes the corresponding mocked method to be re-entered; and when this happens the original code of the real method is executed, without going through the mock again.

If the mock is an instance method mocking a real instance method, it will need access to the mocked instance in order to call back the mocked instance method. For this, the mock class will need to declare an appropriate it field.

A mocked method which has a reentrant mock will have different behavior than if it had a regular mock, so we use the @Mock(reentrant = true) annotation attribute to tell JMockit about our intentions. 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 reenterMockedMethodsThroughItField() throws Exception
   {
      // Create objects to be exercised by the code under test:
      LoginContext loginContext = new LoginContext("test", null, null, configuration);

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

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

      mockInstance.ignoreLogout = true;
      loginContext.logout(); // first entry, won't re-enter
      assertTrue(mockInstance.loggedIn);

      mockInstance.ignoreLogout = false;
      loginContext.logout(); // second entry, will re-enter
      assertFalse(mockInstance.loggedIn);
   }

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

      @Mock(reentrant = true)
      void login() throws LoginException
      {
         try {
            it.login(); // re-enters the mocked method, executing the real code
            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.
            System.out.println("Login attempted for " + it.getSubject());
         }
      }

      @Mock(reentrant = true)
      void logout() throws LoginException
      {
         // A reentrant mock execution can re-enter the mocked method or not,
         // based on a condition.
         if (!ignoreLogout) {
            it.logout();
            loggedIn = false;
         }
      }
   }

In the example above, all real code exercised by the test actually gets executed, even though some methods (login and logout) are mocked. The example indeed is contrived; in practice, reentrant mocks would not normally be useful for testing per se, not directly at least.

You may have noticed that a reentrant mock effectively behaves like advice (from AOP jargon) for the corresponding real method. This is a powerful ability that can be useful for certain things. JMockit itself uses mock classes containing reentrant mocks to provide integration with the JUnit and TestNG test runners.

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 3.8-style tests, these are the setUp() and tearDown() method overrides. In JUnit 4.x 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 of TestNG.

Any mock class that can be applied from inside a test method can also be applied from a "before" method, using one of the "set-up" methods or 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...
}

If we had an annotated mock class instead of a mock-up, the set up would be done with a Mockit.setUpMocks(NamedMockClass.class) call or with a Mockit.setUpMock variant.

The example above uses JUnit 4, but the equivalent code for TestNG or with JUnit 3.8-style tests would be almost 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 of all kinds (annotated with @MockClass or not, mock-up 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 directly instantiated 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. And since it is a mock-up class, it can be applied through simple instantiation 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.