ARTICLE

Introducing Mock Frameworks

From JUnit in Action, Third Edition by Catalin Tudose

Manning Publications
13 min readAug 8, 2020

--

This article discusses widely-used mock frameworks such as EasyMock, JMock, and Mockito.

____________________________________________________________

Take 37% off JUnit in Action, Third Edition. Just enter fcctudose into the discount box at checkout at manning.com.
____________________________________________________________

Introducing Mock frameworks

The engineers at Test It Inc. have been implementing the mock objects from scratch. It isn’t a tedious task, but a frequently recurring one. You might guess that we don’t need to reinvent the wheel every time we need a mock. And you’re right — there are a lot of good projects already written that are out there to help us facilitate the usage of mocks in our projects. In this article we’ll take a closer look on three of the most widely-used mock frameworks — EasyMock, JMock and Mockito. The developers from Test It Inc. want to rework the example HTTP connection application to demonstrate how to use the three frameworks and have a basis for choosing one of the alternatives. You know, people have their own experiences, preferences, and habits and, as they do in these three framework alternatives, they want to put them face-to-face and make some conclusions.

Using EasyMock

Easymock is (http://easymock.org/) an open-source framework which provides useful classes for mocking objects.

In order to work with it, we need to add to the pom.xml file the dependencies from listing 1:

Listing 1 The EasyMock dependencies from the pom.xml configuration file

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>2.4</version>
</dependency>

Trying to introduce EasyMock, the developers from Test It Inc. revise some of their previously constructed mocks.

Listing 2 Reworking the TestAccountService test using EasyMock

[...]
import static org.easymock.EasyMock.createMock; (1)
import static org.easymock.EasyMock.replay; (1)
import static org.easymock.EasyMock.expect; (1)
import static org.easymock.EasyMock.verify; (1)

public class TestAccountServiceEasyMock
{
private AccountManager mockAccountManager; (2)

@Before
public void setUp()
{
mockAccountManager = createMock( “mockAccountManager”,
AccountManager.class ); (3)
}

@Test
public void testTransferOk()
{
Account senderAccount = new Account( "1", 200 ); (4)
Account beneficiaryAccount = new Account( "2", 100 ); (4)

// Start defining the expectations
mockAccountManager.updateAccount( senderAccount ); (5)
mockAccountManager.updateAccount( beneficiaryAccount ); (5)

expect( mockAccountManager.findAccountForUser( "1" ) )
.andReturn( senderAccount ); (6)
expect( mockAccountManager.findAccountForUser( "2" ) )
.andReturn( beneficiaryAccount ); (6)

// we’re done defining the expectations
replay( mockAccountManager ); (7)

AccountService accountService = new AccountService();
accountService.setAccountManager( mockAccountManager );
accountService.transfer( "1", "2", 50 ); (8)

assertEquals( 150, senderAccount.getBalance() ); (9)
assertEquals( 150, beneficiaryAccount.getBalance() ); (9)
}

@After
public void tearDown()
{
verify( mockAccountManager ); (10)
}
}

What we do is:

  1. We start the listing defining the imports from the EasyMock library that we need (1). We see that we heavily rely in static imports.
  2. In (2) we declare the object that we’d like to mock. Notice that our AccountManager is an interface; the reason behind this is simple – the core EasyMock framework can only mock interface objects.
  3. In (3) we call the createMock method to create a mock of the class that we want.
  4. In (4) we create two account objects which we’ll use in our tests. After that, we start declaring our expectations.
  5. With EasyMock we declare the expectations in two ways. When the method return type is voided, we call it on the mock-object (as in (5)), or when the method returns any kind of object, then we need to use the expect-andReturn methods from the EasyMock API (6).
  6. Once we finish defining the expectations, we need to call the replay method to announce it (7).
  7. In (8) we call the transfer method to transfer some money between the two accounts, and in (9) we assert the expected result.
  8. The @After method which gets executed after every @Test method holds the verification of the expectations. With EasyMock we can call the verify method with any mock object (10), to verify that the method-call expectations we declare are triggered. Including the verification into the @After method allows us to easily introduce new tests, and we’ll rely on the execution of the verify method from here.

JUnit best practices: EasyMock object creation

Here’s a nice-to-know tip on the createMock method. If you check the API of EasyMock you see that the createMock method comes with numerous signatures. The signature that we use is

createMock(String name, Class claz);
but there’s also
createMock(Class claz);

Which one should we use? createMock(String name, Class claz) is better. If you use createMock(Class claz) and your expectations aren’t met, you get an error message like the following:

java.lang.AssertionError:
Expectation failure on verify:>
read(): expected: 7, actual: 0

As you see this message isn’t as descriptive as we want it to be. If you use createMock(String name, Class claz)instead, and we map the class to a given name, we get something like the following:

java.lang.AssertionError:
Expectation failure on verify:
name.read(): expected: 7, actual: 0

That was easy, right? How about moving a step forward and revising a more complicated example. No problem, the next listing demonstrates a reworked WebClient test: verifying the correct value returned by the getContent method.

What we want to test is the getContent method of the WebClient. For this purpose, we need to mock all the dependencies to that method. In this example we’ve two dependencies—one is the ConnectionFactory and one is the InputStream. It looks like there’s a problem because EasyMock can only mock interfaces and the InputStream is a class.

To be able to mock the InputStream class we need to use the class extensions of EasyMock. These represent an extension project of EasyMock which lets you generate mock objects[1] for classes and interfaces. They’re addressed by the second Maven dependency from listing 1.

Listing 3 Reworking the WebClient test using EasyMock

[...]
import static org.easymock.classextension.EasyMock.createMock; (1)
import static org.easymock.classextension.EasyMock.replay; (1)
import static org.easymock.classextension.EasyMock.verify; (1)

public class TestWebClientEasyMock
{
private ConnectionFactory factory; (2)
private InputStream stream; (2)

@Before
public void setUp()
{
factory = createMock( “factory”, ConnectionFactory.class ); (3)
stream = createMock( “stream”, InputStream.class ); (3)
}

@Test
public void testGetContentOk() throws Exception
{
expect( factory.getData() ).andReturn( stream );
expect( stream.read() ).andReturn( new Integer( (byte) 'W' ) ); (4)
expect( stream.read() ).andReturn( new Integer( (byte) 'o' ) ); (4)
expect( stream.read() ).andReturn( new Integer( (byte) 'r' ) ); (4)
expect( stream.read() ).andReturn( new Integer( (byte) 'k' ) ); (4)
expect( stream.read() ).andReturn( new Integer( (byte) 's' ) ); (4)
expect( stream.read() ).andReturn( new Integer( (byte) '!' ) ); (4)
expect( stream.read() ).andReturn( -1 ); (4)
stream.close(); (5)

replay( factory ); (6)
replay( stream ); (6)

WebClient2 client = new WebClient2();
String workingContent = client.getContent( factory ); (7)

assertEquals( "Works!", workingContent ); (8)
}

[...]
@Test
public void testGetContentCannotCloseInputStream() throws Exception {
expect( factory.getData() ).andReturn( stream );
expect( stream.read() ).andReturn( -1 );
stream.close(); (9)
expectLastCall().andThrow(new IOException("cannot close")); (10)

replay( factory );
replay( stream );
WebClient2 client = new WebClient2();
String workingContent = client.getContent( factory );

assertNull( workingContent );
}

@After
public void tearDown()
{
verify( factory );
verify( stream );
}
}

In listing 3 we do the following:

  1. We start by importing the objects that we need (1). Notice that because we use the class extensions of EasyMock, we now need to import the org.easymock.classextension.EasyMock object instead of org.easymock.EasyMock. Now we’re ready to create mock objects of classes and interfaces using the statically imported methods of the class extensions.
  2. In (2), as in the previous listings, we declare the objects which we want to mock, and in (3) we call the createMock method to initialize them.
  3. In (4) we define the expectation of the stream when the read method is invoked (notice that to stop reading from the stream, the last thing to return is a -1). Working with a low-level stream, we define how to read one byte at a time, as InputStream is reading byte by byte. In (5) we expect the close method to be called on the stream.
  4. Now we need to denote that we’re done by declaring our expectations — we do this by calling the replay method (6). The replay method is used to pass the mock from the phase where we record the method we expect to be called, to where we test. Before this, we recorded the behavior, but the object isn’t working as a mock. After calling replay, it works as expected. The rest is invoking the method under test (7), and asserting the expected result (8).

We also add another test to simulate a condition when we can’t close the InputStream.

  1. We define an expectation where we expect the close method of the stream to be invoked (9).
  2. Right on the next line we declare that an IOException should be raised if this call occurs (10).

As the name of the framework suggests, using EasyMock is easy, and it’s an option for many projects, but to make you aware of the whole mocking picture, we need to introduce another framework, following the experiments of three developers at Test It Inc., mocking parts of an account service and of a web client using three different mocking frameworks: EasyMock, JMock and Mockito. This way, you have a better taste on how the mocking works.

Using JMock

This far we saw how to implement our own mock-objects and how to use the EasyMock framework. In this section we introduce the JMock framework (http://jmock.org/), to get the full view on the different mocking techniques. We’ll follow the same scenario that the engineers from Test It Inc. are following in order to evaluate the capabilities of a mock framework and compare them with other frameworks: testing money transfer with the help of a mock AccountManager, this time using JMock.

In order to work with JMock, we need to add to the pom.xml file the dependencies from listing 4:

Listing 4 The JMock dependencies from the pom.xml configuration file

<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock-junit5</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock-legacy</artifactId>
<version>2.5.1</version>
</dependency>

As with the previous section, we’ll start with a simple example — reworking listing 8.4 by means of JMock. This is shown in listing 5.

Listing 5 Reworking the TestAccountService test using JMock

[...]
import org.jmock.Expectations; (1)
import org.jmock.Mockery; (1)
import org.jmock.junit5.JUnit5Mockery; (1)

public class TestAccountServiceJMock
{
@RegisterExtension (2)
Mockery context = new JUnit5Mockery(); (2)

private AccountManager mockAccountManager; (3)

@Before
public void setUp()
{
mockAccountManager = context.mock( AccountManager.class ); (4)
}

@Test
public void testTransferOk()
{
Account senderAccount = new Account( "1", 200 ); (5)
Account beneficiaryAccount = new Account( "2", 100 ); (5)

context.checking( new Expectations() (6)
{
{
oneOf( mockAccountManager ).findAccountForUser( "1" ); (7)
will( returnValue( senderAccount ) ); (7)
oneOf( mockAccountManager ).findAccountForUser( "2" );
will( returnValue( beneficiaryAccount ) );

oneOf( mockAccountManager ).updateAccount( senderAccount );
oneOf( mockAccountManager )
.updateAccount( beneficiaryAccount );
}
} );

AccountService accountService = new AccountService();
accountService.setAccountManager( mockAccountManager );
accountService.transfer( "1", "2", 50 ); (8)

assertEquals( 150, senderAccount.getBalance() ); (9)
assertEquals( 150, beneficiaryAccount.getBalance() ); (9)
}
}

Into the listing we do the following:

  1. As always, we start the listing by importing all the necessary objects we need (1). As you can see, unlike EasyMock, the JMock framework doesn’t rely on any static import features.
  2. JUnit5 provides a programmatic way to register extensions. For JMock, this is done by annotating a JUnit5Mockery non-private instance field with @RegisterExtension. The context object serves us to create mocks and to define expectations (2).
  3. In (3) we declare the AccountManager which we want to mock. Like EasyMock, the core JMock framework only mocks interfaces.
  4. In the @Before method, which is executed before each of the @Test methods, we programmatically create the mock by means of the context object (4).
  5. As with any of the previous listings, we declare two accounts which we’ll use to transfer money in between (5).
  6. In (6) we start declaring the expectations, by constructing a new Expectations object.
  7. In (7) we declare the first expectation, each expectation having the form:
invocation-count (mock-object).method(argument-constraints);
inSequence(sequence-name);
when(state-machine.is(state-name));
will(action);
then(state-machine.is(new-state-name));

All the clauses are optional, except for the bold ones — invocation-count and mock-object. We need to specify how many invocations will occur and on which object. After that, in case the method returns an object, we can declare the return object by using the will(returnValue()) construction.

Following this:

  1. In (8) we start the transfer from the one account to the other, and after that we assert the expected results (9). It’s as simple as that!

But wait, what happened with the verification of the invocation count? In all of the previous examples we needed to verify that the invocations of the expectations happened the expected number of times. With JMock you don’t have to do that — the JMock extension takes care of this, and if any of the expected calls aren’t made the test fails.

Following the pattern from the previous section about EasyMock, let’s try and rework it, showing the WebClient test, this time using JMock.

Listing 6 Reworking the TestWebClient test using JMock

[...]

public class TestWebClientJMock
{
@RegisterExtension (1)
Mockery context = new JUnit5Mockery() (1)
{ (1)
{ (1)
setImposteriser( ClassImposteriser.INSTANCE ); (2)
} (2)
}; (2)

@Test
public void testGetContentOk() throws Exception
{
ConnectionFactory factory =
context.mock( ConnectionFactory.class ); (3)
InputStream mockStream =
context.mock( InputStream.class ); (3)

context.checking( new Expectations()
{
{
oneOf( factory ).getData(); (4)
will( returnValue( mockStream ) ); (4)

atLeast( 1 ).of( mockStream ).read(); (5)
will( onConsecutiveCalls( (5)
returnValue( Integer.valueOf ( (byte) 'W' ) ), (5)
returnValue( Integer.valueOf ( (byte) 'o' ) ), (5)
returnValue( Integer.valueOf ( (byte) 'r' ) ), (5)
returnValue( Integer.valueOf ( (byte) 'k' ) ), (5)
returnValue( Integer.valueOf ( (byte) 's' ) ), (5)
returnValue( Integer.valueOf ( (byte) '!' ) ), (5)
returnValue( -1 ) ) ); (5)

oneOf( mockStream ).close();
}
} );

WebClient2 client = new WebClient2();
String workingContent = client.getContent( factory ); (6)

assertEquals( "Works!", workingContent ); (7)
}

@Test
public void testGetContentCannotCloseInputStream() throws Exception
{

ConnectionFactory factory =
context.mock( ConnectionFactory.class );
InputStream mockStream = context.mock( InputStream.class );

context.checking( new Expectations()
{
{
oneOf( factory ).getData();
will( returnValue( mockStream ) );
oneOf( mockStream ).read();
will( returnValue( -1 ) );
oneOf( mockStream ).close(); (8)
will( throwException(
new IOException( "cannot close" ) ) ); (9)
}
} );

WebClient2 client = new WebClient2();

String workingContent = client.getContent( factory );

assertNull( workingContent );
}
}

Into the listing above we do the following:

  1. We start the test-case by registering the JMock extension. The JUnit5Mockery non-private instance field context is annotated with @RegisterExtension (1).
  2. To tell JMock to create mock objects not only for interfaces, but also for classes, we need to set the imposteriser property of the context (2). Now we can continue creating mocks the normal way.
  3. In (3) we declare and programmatically initialize the two objects we want to create mocks of.
  4. In (4) we start declaration of the expectations. Notice the fine way we declare the consecutive execution of the read() method of the stream (5), and also the returned values.
  5. In (6) we call the method under test and in (7) we assert the expected result.
  6. For a full view of how to use the JMock mocking library, we also provide another @Test method, which tests our WebClient under exceptional conditions. In (8) we declare the expectation of the close() method being triggered and in (9) we instruct JMock to raise an IOException when this trigger happens.

As you can see, the JMock library is as easy to use as the EasyMock one, but provides better integration with JUnit 5 — we can programmatically register the Mockery context field. We still need to take a look at the third proposed framework: Mockito. We’re going to see that this one is closer to the JUnit 5 paradigm!

Using Mockito

We nkow how to work with EasyMock and JMock. Let’s introduce the Mockito framework (https://site.mockito.org/), another popular mocking framework. This is also considered by the engineers at Test It Inc. to evaluate it and to eventually introduce it into their projects.

In order to work with Mockito, you need to add to the pom.xml file the dependency from listing 7:

Listing 7 The Mockito dependency from the pom.xml configuration file

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.21.0</version>
<scope>test</scope>
</dependency>

As for EasyMock and JMock, we’ll rework the example from listing 8.4 (testing money transfer with the help of a mock AccountManager), this time by means of Mockito. This is shown in listing 8.

Listing 8 Reworking the TestAccountService test using Mockito

[...]

import org.junit.jupiter.api.extension.ExtendWith; (1)
import org.mockito.Mock; (1)
import org.mockito.Mockito; (1)
import org.mockito.junit.jupiter.MockitoExtension; (1)


@ExtendWith(MockitoExtension.class) (2)
public class TestAccountServiceMockito
{

@Mock (3)
private AccountManager mockAccountManager; (3)

@Test
public void testTransferOk()
{
Account senderAccount = new Account( "1", 200 ); (4)
Account beneficiaryAccount = new Account( "2", 100 ); (4)

Mockito.lenient().when(mockAccountManager.findAccountForUser("1")) (5)
.thenReturn(senderAccount); (5)
Mockito.lenient().when(mockAccountManager.findAccountForUser("2")) (5)
.thenReturn(beneficiaryAccount); (5)

AccountService accountService = new AccountService();
accountService.setAccountManager( mockAccountManager );
accountService.transfer( "1", "2", 50 ); (6)

assertEquals( 150, senderAccount.getBalance() ); (7)
assertEquals( 150, beneficiaryAccount.getBalance() ); (7)
}
}

Into the listing above we do the following:

  1. As usual, we start the listing by importing all the necessary objects we need (1). This example using the Mockito framework doesn’t rely on static import features.
  2. We extend this test using MockitoExtension (2). @ExtendWith is a repeatable annotation which is used to register extensions for the annotated test class or test method. For this Mockito example, we’ll only note that this extension is needed in order to create the mock objects through annotations, as we do at (3). This tells Mockito to create a mock object of type AccountManager.
  3. Like any of the previous listings, we declare two accounts which we’ll use to transfer money in between (4).
  4. In (5) we start declaring the expectations, by using the when method. Additionally, we use the lenient method in order to modify the strictness of objects mocking. Without this one, only one expectation declaration is allowed for the same findAccountForUser method, but we need two (one for the “1” argument and one for the “2” argument).
  5. In (6) we start the transfer from the one account to the other, and after that we assert the expected results (7).

Following the pattern from the previous sections about EasyMock and JMock, let’s try and rework it, showing the WebClient test, this time using Mockito.

Listing 9 Reworking the TestWebClient test using Mockito

[...]import org.mockito.Mock;                                                   (1)import org.mockito.junit.jupiter.MockitoExtension;                         (1)import static org.mockito.Mockito.doThrow;                                 (1)import static org.mockito.Mockito.when;                                    (1)@ExtendWith(MockitoExtension.class)                                        (2)
public class TestWebClientMockito
{
@Mock (3)
private ConnectionFactory factory; (3)

@Mock (3)
private InputStream mockStream; (3)

@Test
public void testGetContentOk() throws Exception
{
when(factory.getData()).thenReturn(mockStream); (4)
when(mockStream.read()).thenReturn((int) 'W') (5)
.thenReturn((int) 'o') (5)
.thenReturn((int) 'r') (5)
.thenReturn((int) 'k') (5)
.thenReturn((int) 's') (5)
.thenReturn((int) '!') (5)
.thenReturn(-1); (5)

WebClient2 client = new WebClient2();

String workingContent = client.getContent( factory ); (6)

assertEquals( "Works!", workingContent ); (7)
}

@Test
public void testGetContentCannotCloseInputStream()
throws Exception
{
when(factory.getData()).thenReturn(mockStream); (8)
when(mockStream.read()).thenReturn(-1); (9)
doThrow(new IOException( "cannot close" )) (10)
.when(mockStream).close(); (10)

WebClient2 client = new WebClient2();

String workingContent = client.getContent( factory );

assertNull( workingContent );
}
}

Into the listing above we do the following:

  1. We import the needed dependencies, static and non-static in this example (1).
  2. We extend this test using MockitoExtension (2). For this Mockito example, this extension is needed in order to be able to create the mock objects through annotations, as we do at (3). This tells Mockito to create one mock object of type ConnectionFactory and one mock object of type InputStream.
  3. In (4) we start the declaration of the expectations. Notice the fine way we declare the consecutive execution of the read() method of the stream (5), and also the returned values.
  4. In (6) we call the method under test and in (7) we assert the expected result.
  5. We also provide another @Test method, which tests our WebClient under exceptional conditions. In (8) we declare the expectation of the factory.getData() method, and in (9) we declare the expectation of the mockStream.read() method. In (10) we instruct Mockito to raise an IOException when we close the stream.

As you can see, the Mockito framework may be used with the new JUnit 5 extension model — and not programmatically, as JMock, but through the use of the JUnit 5 @ExtendWith and of the Mockito @Mock annotation.

That’s all for this article.

If you want to learn more about the book, check it out on our browser-based liveBook reader here.

[1] final and private methods can’t be mocked.

--

--

Manning Publications
Manning Publications

Written by Manning Publications

Follow Manning Publications on Medium for free content and exclusive discounts.

No responses yet