An example

Lets say we have a business service class which provides a business operation with the following responsibilities: a) it finds certain persistent entities needed by the operation; b) it persists the state for a new entity; and c) it sends a notification e-mail to an interested party. The first two responsibilities require access to the application database, which is done through a simplified API to the persistence subsystem. The third one can be achieved with a third-party API for sending e-mail, which in this example is the Commons Email library.

Therefore, the service class has two separate dependencies, one for persistence and another for e-mail. In order to unit test the business operation while verifying proper interaction with these collaborators, we will use the available mocking APIs. Complete source code for a working solution - with all tests - is available online and under samples/tutorial in the full distribution zip file.

package jmockit.tutorial.domain;

import java.math.*;
import java.util.*;
import org.apache.commons.mail.*;
import static jmockit.tutorial.persistence.Database.*;

public final class MyBusinessService
{
   public void doBusinessOperationXyz(EntityX data) throws EmailException
   {
      List<EntityX> items =
(1)      find("select item from EntityX item where item.someProperty = ?1", data.getSomeProperty());

      // Compute or obtain from another service a total value for the new persistent entity:
      BigDecimal total = ...
      data.setTotal(total);

(2)   persist(data);

      sendNotificationEmail(data, items);
   }

   private void sendNotificationEmail(EntityX data, List<EntityX> items) throws EmailException
   {
      Email email = new SimpleEmail();
      email.setSubject("Notification about processing of ...");
(3)   email.addTo(data.getCustomerEmail());

      // Other e-mail parameters, such as the host name of the mail server, have defaults defined
      // through external configuration.

      String message = buildNotificationMessage(items);
      email.setMsg(message);

(4)   email.send();
   }

   private String buildNotificationMessage(List<EntityX> items) { ... }
}

The Database class contains only static methods and a private constructor; the find and persist methods should be obvious, so we won't list them here (assume they are implemented on top of an ORM API, such as Hibernate or JPA).

So, how can we unit test the "doBusinessOperationXyz" method without making any changes to the existing application code? JMockit actually provides three different mocking APIs, each capable enough for our needs. (In practice, the first two APIs are just subsets of a larger behavior-oriented mocking API; the other one, which can be described as an state-oriented mocking API, is truly different and unique.) We will see how to use each one of them in the following sections. In every situation, a JUnit test case will verify the invocations of interest made from the unit under test to its external dependencies. These invocations are the ones at points (1)-(4) indicated above.

Using the Expectations API

First, lets use the JMockit Expectations API.

package jmockit.tutorial.domain;

import org.apache.commons.mail.*;
import jmockit.tutorial.persistence.*;

import org.junit.*;
import mockit.*;

public final class MyBusinessService_ExpectationsAPI_Test
{
   @Mocked(stubOutClassInitialization = true) final Database unused = null;
   @Mocked SimpleEmail email;

   @Test
   public void doBusinessOperationXyz() throws Exception
   {
      final EntityX data = new EntityX(5, "abc", "abc@xpta.net");

      // Recorded strictly, so matching invocations must be replayed in the same order:
      new Expectations() {{
(1)      Database.find(withSubstring("select"), any);
         result = new EntityX(1, "AX5", "someone@somewhere.com");

(2)      Database.persist(data);
      }};

      // Recorded non-strictly, so matching invocations can be replayed in any order:
      new NonStrictExpectations() {{
(4)      email.send(); times = 1; // a non-strict invocation requires a constraint if expected
      }};

      new MyBusinessService().doBusinessOperationXyz(data);
   }

   @Test(expected = EmailException.class)
   public void doBusinessOperationXyzWithInvalidEmailAddress() throws Exception
   {
      new NonStrictExpectations() {{
(3)      email.addTo((String) withNotNull()); result = new EmailException();

         // If the e-mail address is invalid, sending the message should not be attempted:
         email.send(); times = 0;
      }};

      EntityX data = new EntityX(5, "abc", "someone@somewhere.com");
      new MyBusinessService().doBusinessOperationXyz(data);
   }
}

First of all, note that what gets mocked is specified through mock fields, which in this case are instance fields of the test class annotated as @Mocked. A mock field can be of any reference type: an interface, an abstract class, a final/non-final class, an enum type, an annotation type, or even a generic type parameter. We say that such a mock field has a mocked type. In truth, it is the type specified to be mocked that actually matters; the field itself is not essential, although very useful when the test needs a mocked object to record expectations on. Such a mocked instance will be automatically created and assigned to the field, so it will never be left with a null reference. It is possible to "take over" the value assigned to the mock field, though, by simply declaring it as final.

By default, all methods of a class declared as a mocked type will be mocked, that is, they will be modified so that the actual method implementation in production code is never executed as long as the type stays mocked. The same holds true for the constructors of a mocked class. Class initialization code (in static blocks and/or non-compile time assignments to static fields) can be stubbed out when needed, as done above for the Database class. (This isn't done by default because the JVM can only initialize a class once, potentially resulting in static fields left null when the class is initialized while mocked.)

Notice that JMockit Expectations does not create any mock subclasses for concrete mocked classes; instead, it directly modifies the actual implementation of the mocked class. On the other hand, methods of an interface and abstract methods do not contain any implementation to be modified. In such cases, an empty implementation body is provided in a concrete implementation class. This is all done automatically by JMockit in a transparent way, with no additional effort on the part of the developer. Finally, note that this makes every instance of a mocked class a mocked instance, for as long as the class remains mocked - there is no separate "mock object" per se.

As the example tests show, expected or allowed invocations are specified through regular method and constructor invocations on mocked classes or mock fields, while inside an Expectations or NonStrictExpectations block (which is an anonymous inner class containing an instance initialization block). Each such invocation partly defines an expectation for the test.

We say that such invocations or expectations are recorded during a record phase, so that they can be replayed (and optionally verified) later, during a replay phase. Typically, this second phase of the test starts with the call that exercises the code under test, and lasts until the test execution completes. This is known as the record-replay model of test execution. Note that the record/replay dichotomy applies to each individual expectation, not to the test as whole. That is, a test method can switch between the record and replay phases any number of times with different expectations; doing so, however, would lead to confusing tests and is therefore highly discouraged, even if valid.

Additional behavior can and often is specified for an expectation. This is done by assigning values to certain fields and/or calling certain methods of the Expectations API, after the recorded method/constructor invocation of interest, and before the next expectation to appear in the block. A recorded return value of any type can be assigned to the special result field. Similarly, a recorded exception (or error) can be specified by assigning the desired Throwable instance to this same result field.

The method calls "withXyz(...)", as well as the field accesses "anyAbc", allow flexible matching of argument values for the recorded invocations. Otherwise, an exact match is expected between recorded argument values and the corresponding values received in the replay phase.

All expectations recorded inside Expectations blocks are strictly verified before the end of the test. Non-strict expectations (those appearing inside a NonStrictExpectations block) are only verified if an explicit invocation count constraint is specified in the test, through an assignment to the times field (or alternatively to the minTimes and/or maxTimes fields). A java.lang.AssertionError will be thrown for any expected invocation which did not happen during the replay phase. An error will also be thrown for any invocation to a mocked type that does occur during the replay phase, but which was expected to not occur; all the unrecorded invocations to strictly mocked types will cause such an error, and also the ones recorded (strictly or not) with a minimum invocation count which gets violated at replay time. Even more, the test will fail (again, with an AssertionError) if expected invocations to one or more strict mocked types are replayed in a different order than they were recorded.

There is more to the Expectations API, about which you can find in the chapter on behavior-based testing, in the API documentation, and also in the many additional sample tests inside the full distribution.

Using the Verifications API

Up to this point we focused on expectations that are recorded/specified before exercising the code under test. Such expectations are verified implicitly (when strict or with an specified invocation count constraint) or not at all (when non-strict and without any associated invocation count constraint). Lets now consider the use of the JMockit Verifications API, which lets us explicitly verify expectations after having exercised the code under test.

package jmockit.tutorial.domain;

import org.apache.commons.mail.*;
import jmockit.tutorial.persistence.*;

import org.junit.*;
import mockit.*;

public final class MyBusinessService_VerificationsAPI_Test
{
   @Tested MyBusinessService service; // instantiated automatically
   @Mocked(stubOutClassInitialization = true) Database onlyStatics;
   @Capturing Email email; // concrete subclass mocked on demand, when loaded

   final EntityX data = new EntityX(5, "abc", "someone@somewhere.com");

   @Test
   public void doBusinessOperationXyzPersistsData() throws Exception
   {
      // No expectations recorded in this case.

      service.doBusinessOperationXyz(data);

(2)   new Verifications() {{ Database.persist(data); }};
   }

   @Test
   public void doBusinessOperationXyzFindsItemsAndSendsNotificationEmail() throws Exception
   {
      // Invocations that produce a result are recorded, but only those we care about.
      new NonStrictExpectations() {{
(1)      Database.find(withSubstring("select"), (Object[]) null);
         result = new EntityX(1, "AX5", "someone@somewhere.com");
      }};

      service.doBusinessOperationXyz(data);

      new VerificationsInOrder() {{
(3)      email.addTo(data.getCustomerEmail());
(4)      email.send();
      }};
   }
}

To recapitulate, non-strict expectations differ from strict ones in that they can be executed or not by code under test, even if they were not previously recorded. And if they are executed the relative order between invocations doesn't matter, unless specified otherwise.

The example tests above show two ways to have non-strict expectations: first, by simply having no expectation block at all; second, by specifying expectations inside a NonStrictExpectations block. In the first case, all invocations to mocked types made in production code will be allowed without restrictions, but non-void mocked methods will always return a fixed default value according to the method return type. Obviously, this won't always be appropriate, so an expectation block of some kind containing recorded invocations will be needed.

When we use non-strict expectations, invocations to mocked methods and constructors during the replay phase are not immediately verified (unless explicitly specified otherwise through an invocation count constraint). Those non-strict invocations which were recorded with an specific return value or with an exception/error to be thrown will produce the expected result if and when replayed by production code.

The fact that an explicitly recorded non-strict expectation allows the test to pass is usually enough evidence to demonstrate that the tested unit did the right thing. In the second test above for example, suppose the line recording a result for the Database.find(...) invocation is commented out. The test will likely fail when another part of the code under test which depends on that returned value is executed, or when an expected invocation is verified in the test itself (in this example test, an extra email.setMsg(withNotEqual("")); verification would be needed between the other two verified invocations). However, in some cases you may want to be sure that the invocation will occur at least once. To achieve that, simply specify it with a minTimes = 1 constraint after the recorded invocation in the non-strict expectation block.

Another advantage of non-strict expectations is that they can be explicitly verified in test code. Such verifications are just like the recording of invocations: simply invoke the method or constructor to be verified, but inside a Verifications block (or a subclass such as VerificationsInOrder, which also verifies the relative order of execution). Such blocks must always be executed after the code under test was exercised, that is, after the end of the replay phase. Note that verification blocks will typically contain invocations to constructors and methods of void return type. Non-void methods can be explicitly verified as well (even if recorded previously in the test), although as we saw before they will also be implicitly verified if an invocation count constraint was specified in the record phase.

Syntactically, a verification block is just like an expectation block. Invocations to mocked types are immediately verified to match corresponding invocations made during the replay phase. Argument matching constraints can be used so that a single invocation in a verification block verifies several invocations executed by the code under test. The invocation count for a particular method or constructor can be verified by specifying constraints through one of the times, minTimes, and maxTimes fields, just like it can be done for expectations in the recording phase. In particular, it can be verified that an specific invocation never happened by specifying a times = 0 invocation count constraint (or maxTimes = 0 if preferred).

Using the Mockups API

Next, lets see how the JMockit Mockups API solves the problem.

package jmockit.tutorial.domain;

import java.util.*;
import org.apache.commons.mail.*;
import jmockit.tutorial.persistence.*;

import static org.junit.Assert.*;
import org.junit.*;
import mockit.*;

public final class MyBusinessService_MockupsAPI_Test
{
   public static final class MockDatabase extends MockUp<Database>
   {
      @Mock
      public void $clinit() { /* do nothing */ }

      @Mock(invocations = 1)
(1)   public List<EntityX> find(String ql, Object... args)
      {
         assertNotNull(ql);
         assertTrue(args.length > 0);
         return Arrays.asList(new EntityX(1, "AX5", "someone@somewhere.com"));
      }
   
      @Mock(maxInvocations = 1)
(2)   public void persist(Object o) { assertNotNull(o); }
   }

   @BeforeClass
   public static void mockUpPersistenceFacade()
   {
      // Applies the mock class by invoking its constructor:
      new MockDatabase();
   }

   final EntityX data = new EntityX(5, "abc", "5453-1");

   @Test
   public void doBusinessOperationXyz() throws Exception
   {
      // Defines and applies a mock class in one operation:
      new MockUp<Email>() {
         @Mock(invocations = 1)
         Email addTo(Invocation inv, String email)
         {
            assertEquals(data.getCustomerEmail(), email);
            return inv.getInvokedInstance();
         }

         @Mock(invocations = 1)
(4)      String send() { return ""; }
      };
   
      new MyBusinessService().doBusinessOperationXyz(data);
   }

   @Test(expected = EmailException.class)
   public void doBusinessOperationXyzWithInvalidEmailAddress() throws Exception
   {
      new MockUp<Email>() {
         @Mock
(3)      Email addTo(String email) throws EmailException
         {
            assertNotNull(email);
            throw new EmailException();
         }
      
         @Mock(invocations = 0)
         String send() { return null; }
      };
   
      new MyBusinessService().doBusinessOperationXyz(data);
   }
}

Here, instead of recording or verifying expectations through invocations to mocked types, we directly specify mock implementations for the methods and constructors of interest. Such mock methods must have the same signature as the "real" methods and constructors, and be annotated with @Mock. They are defined in a mock class, which can either be a separate class (nested or not) or an anonymous inner class defined inside a test method; in either case, it must extend the generic MockUp<T> base class, while providing the type to be mocked as the "value" for the type parameter T.

The two tests above share a reusable mock class, MockDatabase, which is applied to the test class as a whole in an @BeforeClass method. Notice that we also stub out static initializers in the Database class, by defining the special mock method "$clinit()". This is necessary in this case because the Database class actually creates a JPA EntityManagerFactory instance during its static initialization.

Each test sets up specific mocks for the Email class by creating an inline (anonymous) mock-up class instance. As seen in some of these mock methods, the @Mock annotation can optionally specify exact/min/max constraints on the expected/allowed number of invocations to the corresponding real method. Although not shown here, constructors can be mocked with mock methods named "$init" and having the same parameters as the constructor to be mocked.

Typically, the majority of tests can be written with the Expectations & Verifications API. There are situations, however, where the Mockups API can be an useful complement to that.