JMockit Tutorial: Expectations and Verifications

Learn to create and inject mocks, creating expectations and verifications using JMockit library in JUnit tests. We will begin with the basic concepts of JMockit with an example and later dig deeper into the advanced concepts.

1. JMockit’s Core Concepts

1.1. Core Features

JMockit is open-source software that contains support for mocking, faking, and integration testing, and a code coverage tool. It is used for mocking the external dependencies outside the test boundary, similar to Mockito and other such mocking libraries.

The most important feature of JMockit is that it lets us mock anything, even the things that are hard to mock with other libraries such as private methods, constructors, static and final methods. It even allows mocking the member fields and initialization blocks as well.

1.2. Test Stages

Similar to EasyMock, JMockit also uses the Record-Replay-Verify model in a test after the mocks and SUT (System Under Test) have been defined.

  • Record: In this step, we record the expectations from the mock objects. We define the behavior of mock objects i.e. method to be invoked, the return value and how many times we expect it to be invoked.
  • Replay: In this step, we execute the actual test code as written in SUT (System Under Test).
  • Verify: In this step, we verify whether all the expectations were executed or not.

A typical JMockit test will look like this:

public class TestClass {

	@Tested
	private Service service;

	@Injectable
	private Dao dao;

	@Mock
	private Component component;

	@Test
	public void testSUT() {
	   // Test data initialization, if any

	   new Expectations() {{ 
	       // define expected behaviour for mocks and injectables
	   }};

	   // test service operations

	   new Verifications() {{ 
	       // verify mocks and injectables
	   }};

	   // assertions
	}	
}

1.3. Declarative Expectations and Verifications

JMockit allows defining the expectations and verifications in a very elaborative and declarative manner. These are very easy to distinguish from the rest of the test code.

Other mocking libraries, in general, provide static methods such as expect(), andThenReturn() and times() to specify the expectations, and verify() for verifying the expectations after the test execution.

MockAPI.expect(mock.method(argumentMatcher)).andThenReturn(value).times(1);

In contrast, JMockit expresses them using the following classes:

  • Expectations: An Expectations block represents a set of invocations to a specific mocked method/constructor that is relevant for a given test.
  • Verifications: A regular unordered block to check that at least one matching invocation occurred during replay.
  • VerificationsInOrder: It should be used when we want to test the actual relative order of invocations during the replay phase.
  • FullVerfications: If we want to have all invocations to the mocked types/instances involved in a test verified. It will make sure that no invocations are left unverified.

We will revisit these classes again later in this tutorial.

2. A Simple JMockit Test Example

2.1. Maven Dependency

Start with including the JMockit dependency in the application. If not included already, add JUnit dependencies as well.

<dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.49</version>
</dependency>

2.2. System Under Test

To demo the JMockit syntax, we have created a typical usecase where a RecordService invokes RecordDao to save a Record, and send a notification to using NotificationService. The RecordService uses a SequenceGenerator class to get the next record id.

You can browse the code in the GitHub repository whose link is present at the end of this tutorial.

2.3. Test Demo

To test RecordService.saveRecord() method, we need to inject RecordDao and SequenceGenerator as dependencies in it. The RecordService gets NotificationService instance in runtime so we can simply mock it and let runtime replace it with a mock.

Next, we will create some Expectations, execute the test code and, finally, execute Verifications to conclude the test. We can use additional JUnit assertions to verify additional test outcomes.

public class JMockitDemoTests {

  @Injectable
  RecordDao mockDao;	// Dependency

  @Injectable
  SequenceGenerator mockGenerator; // Dependency

  @Tested
  RecordService service;	//System Under Test

  // NotificationService can be mocked in test scope
  @Test
  public void testSaveRecord(@Mocked NotificationService notificationService) {

    Record record = new Record();
    record.setName("Test Record");

    //Register Expectations
    new Expectations() {{
      mockGenerator.getNext();
      result = 100L;
      times = 1;
    }};

    new Expectations() {{
      mockDao.saveRecord(record);
      result = record;
      times = 1;
    }};

    new Expectations() {{
      notificationService.sendNotification(anyString);
      result = true;
      times = 1;
    }};


    //Test code
    Record savedRecord = service.saveRecord(record);

    // Verifications
    new Verifications() {{ // a "verification block"
      mockGenerator.getNext();
      times = 1;
    }};

    new Verifications() {{
      mockDao.saveRecord(record);
      times = 1;
    }};

    new Verifications() {{
      notificationService.sendNotification(anyString);
      times = 1;
    }};

    //Additional assertions
    assertEquals("Test Record", savedRecord.getName());
    assertEquals(100L, savedRecord.getId());
  }
}

3. Creating and Injecting Mocks

It is worth remembering that JMockit allows having different mock objects in different phases on the record-replay-verify flow. For example, we can have two mocks for a dependency and use them separately in expectations and verifications.

Differently from other mocking APIs, these mocked objects don’t have to be the ones used by the code under test when it calls instance methods on its dependencies.

@Mocked Dependency mockDependency;

@Test
public void testCase(@Mocked Dependency anotherMockDependency)
{
	new Expectations() {{ 
      mockDependency.operation();
   }};

   // Call the code under test

   new Verifications() {{ 
      anotherMockDependency.operation();
   }};
}

JMockit allows different ways to create and inject mocks for SUT. Let’s learn about them.

3.1. JMockit Annotations

The primary annotations for mocking the dependencies are as follows.

3.1.1. @Mocked and @Capturing

When used on a field, @Mocked will create mocked instances of each and every new object of that particular class during the test execution. Internally, it will mock all methods and constructors on all instances of a mocked class.

@Mocked Dependency mockDependency;

@Capturing behaves similar to @Mocked, but additionally, @Capturing mocks every subclass extending or implementing the annotated field’s type.

In the following example, JMockit will mock all the instances of Dependency as well as any subclasses of it. If Dependency is an interface then JMockit will mock all its implementing classes.

@Capturing Dependency mockDependency;

Note that the mock fields annotated only with @Mocked or @Capturing are not considered for injection.

3.1.2. @Injectable and @Tested

The @Tested annotation triggers the automatic instantiation and injection of other mocks and injectables, just before the execution of a test method. An instance will be created using a suitable constructor of the tested class, while making sure its internal @Injectable dependencies get properly injected (when applicable).

As opposed to @Mocked and @Capturing, @Injectable creates only one mocked instance.

Note that while initializing the tested classes, JMockit supports two forms of injection: i.e. constructor injection and field injection.

In the following example, dep1 and dep2 will be injected into SUT.

public class TestClass {

   @Tested SUT tested;

   @Injectable Dependency dep1;
   @Injectable AnotherDependency dep2;
}

3.2. Test Class and Method Scoped Mocks

JMockit allows creating mocks at the class level as well as at the test method level by passing the mocks as test parameters. Method-level mocks help in creating a mock just for one test and thus help in further limiting the test boundaries.

public class TestClass {

	//Class scoped mock
  @Mocked Dependency mock;

  //Method scoped mock
  @Test
	public void testCase(@Mocked AnotherDependency anotherMock)
	{
		//test code
	}
}

4. Recording Expectations

4.1. Matching Method Invocations

JMockit is very flexible in recording expectations. We can record multiple method invocations in a single Expectations block and also, and we can record multiple Expectations blocks in a single test method.

public TestClass {

	new Expectations() {{
		mock.method1();
		mock.method2();
		anotherMock.method3();
	}};

	new Expectations() {{
		someOtherMock.method();
	}};
}

4.2. Matching Arguments

Using the exact arguments in method invocations will match the exact argument values in the replay phase. The object-type arguments are checked equal using the equals() method. Similarly, arrays and lists type arguments are treated as equals if both arrays or lists are of the same size and contain similar elements.

For flexible argument matching, we can use one of the following two approaches:

4.2.1. any Fields

JMockit provides a range of any argument matching fields. They support one for each primitive type (and the corresponding wrapper class), one for strings, and one for all Objects.

new Expectations() {{

  mock.method1(anyInt);
  mock.method2(anyString);
  mock.method3(anyInt);
  mock.method4((List<?>) any);
  mockDao.saveRecord((Record) any);
}};

4.2.2. with Methods

We can use a withXYZ() method from a range of such methods for specific usages. These methods are withEqual(), withNotEqual(), withNull(), withNotNull(), withSubstring(), withPrefix(), withSuffix(), withMatch(regex), withSameInstance(), withInstanceLike() and withInstanceOf() etc.

new Expectations() {{

  mock.method1(withSubstring("xyz"));
  mock.method2(withSameInstance(record));
  mock.method3(withAny(1L));	//Any long value will match
  mock.method4((List<?>) withNotNull());
}};

4.3. Matching Return Values

If non-void mock methods, we can record the return values in result field. The assignment to result should appear right after the invocation that identifies the recorded expectation.

new Expectations() {{
	mock.method1();
	result = value1;

	mock.method2();
	result = value2;
}};

If we are invoking a method in a loop then we can expect multiple return values either using returns(v1, v2, …) method or assigning a list of values to result field.

new Expectations() {{
	mock.method();
	returns(value1, value2, value3);
}};

If the test instead needs an exception or error to be thrown when the method is invoked, simply assign the desired throwable instance to result.

new Expectations() {{

	mock.method();
	result = new ApplicationException();
}};

4.3. Matching Invocation Count

JMockit provides three special fields just matching the invocation counts. Any invocations less or more than the expected lower or upper limit, respectively, and the test execution will automatically fail.

  • times
  • minTimes
  • maxTimes
new Expectations() {{
	mock.method();
	result = value;
	times = 1;
}};

5. Writing Verifications

5.1. Verifications

Inside a Verifications blocks, we can use the same steps that are available in a Expectations blocks except for the return values and thrown exceptions. We can reuse the method invocations and count from the expectations.

So, the syntax to write verifications is the same as expectations and you can refer to previous sections for the same.

new Verifications() {{
	mock.method();
	times = 1;
}};

5.2. VerificationsInOrder

As mentioned in section 1.3, this helps to test the actual relative order of invocations during the replay phase. Inside this block, simply write the invocations to one or more mocks in the order they are expected to have occurred.

@Test
public void testCase() {
	//Expectation

	//Test code
	mock.firstInvokeThis();
	mock.thenInvokeThis();
	mock.finallyInvokeThis();

	//Verification
	new VerificationsInOrder() {{
	  mock.firstInvokeThis();
	  mock.thenInvokeThis();
	  mock.finallyInvokeThis();
	}};
}

5.3. FullVerifications

In the previous verification modes, JMockit verifies that all invocations in the Verifications block must be executed at least once during the test replay phase. It does not complain about those invocations that happened in the replay phase but were not added in the Verifications block.

In the following example, method3() has been executed in the test but not verified in the verification phase. The test will PASS.

@Test
public void testCase() {

	//Test code
	mock.method1();
	mock.method2();
	mock.method3();

	//Verification
	new VerificationsInOrder() {{
		mock.method1();
		mock.method2();
	}};
}

If we want to take total control of the mock interactions then we can FullVerifications. It helps in preventing any method to be executed that we are not verifying.

@Test
public void testCase() {

	//Test code
	mock.method1();
	mock.method2();
	mock.method3();

	//Verification
	new FullVerifications() {{
		mock.method1();
		mock.method2();
		mock.method3();		//If we remove this, the test will FAIL
	}};
}

6. Conclusion

In this tutorial, we learned to use the mocking functionality provided by JMockit, in detail. We learned about the record-replay-verify phases in-depth and with examples.

We also learned the advanced concepts such as flexible argument matching and invocation counts.

Happy Learning !!

Sourcecode on Github

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode