Mocking a constructor. A unit test for a factory method.

Introduction

In our previous post we have learnt the basics of using Mockito to write true unit tests.
Now we are going to unit test a factory method. The particular implementation used here is the one described in the traditional Data Access Object pattern for the DAO Factory.
In the process we’re going to stumble upon some Mockito’s limitations and we’ll see a way to overcome them.

The factory method

Let’s suppose we have a typical DAOFactory class exposing a factory method with the following signature:

public static DAOFactory getDAOFactory(Type type)

The method is meant to be called by clients in order to get a DAO Factory implementation for a particular database vendor. For instance, by calling DAOFactory.getDAOFactory(DAOFactory.Type.MARIADB) we’d get an instance of the class MariaDbDAOFactory, suited to work with a MariaDB database.

The test

Then let’s write a unit test for that scenario: obtaining a MariaDbDAOFactory object by calling the factory method with the type MARIADB.
First of all, we can establish the following success condition for the test: it will pass if and only if the method creates and return a MariaDbDAOFactory instance when it’s called with a MARIADB type as the parameter.
By following the naming conventions advised in xUnit Test Patterns, we can start writing our method like this:

@Test
public void testGetDAOFactoryMariaDb_shouldCreateAndReturnMariaDbDAOFactory() throws Exception {

	// XUnit Phase 1: Setup

	// XUnit Phase 2: Exercise

	// XUnit Phase 3: Verification

}

I have also written some comments as placeholders to better organise the testing code according to the XUnit phases (teardown phase is not necessary here).
Since we want a true unit test, we should mock the collaborating classes, like MariaDbDAOFactory.
When writing tests, most of the time we’ll be dealing with regular method calls on an instance of a collaborating class. But this case is different, since the interaction consists of invoking a constructor of the class to be mocked.
This is where the difficulty lies, since Mockito cannot mock constructors. We must adopt, therefore, a different approach.

Enter PowerMock

The framework PowerMock extends other mocking libraries with advanced features, like mocking constructors, static methods, final methods, etc.
One of the extended libraries happens to be Mockito. This allows us to utilise PowerMock while leveraging our experience with Mockito.
Now that we have the tool we need to mock constructors, let’s see how to use it.
Before going further, if you use Maven, these are the dependencies you’re going to need:

  	<dependency>
  		<groupId>org.powermock</groupId>
  		<artifactId>powermock-api-mockito</artifactId>
  		<version>1.5</version>
  		<scope>test</scope>
  	</dependency>
  	<dependency>
  		<groupId>org.powermock</groupId>
  		<artifactId>powermock-module-junit4</artifactId>
  		<version>1.5</version>
  		<scope>test</scope>
  	</dependency>

Now, let’s prepare the test class. PowerMock tests run with their own JUnit runner: PowerMockRunner.
Also, since PowerMock’s extended features requires byte-code manipulation, it’s also necessary to specify which classes will be prepared in that way. The annotation @PrepareForTest allows us to do it.
Now, our test class looks like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DAOFactory.class)
public class DAOFactoryTest {
[...]

Note that the class to be prepared is the class invoking the constructor, and not the class to be instantiated.
Next, we need to record the mock behaviour, which we’ll do in two steps:

  1. Creating the mocked new instance of MariaDbDAOFactory. For this purpose we’ll use PowerMockito.mock instead of Mockito.mock
  2. Specifying how the constructor invocation is going to behave. Basically, we are going to tell PowerMock to return our recently created mocked instance whenever the constructor is called.

This is the actual code:

		// XUnit Phase 1: Setup
		MariaDbDAOFactory mockedDAOFactory = PowerMockito.mock(MariaDbDAOFactory.class);

		PowerMockito.whenNew(MariaDbDAOFactory.class)
					.withNoArguments()
					.thenReturn(mockedDAOFactory);

Now we can exercise the method under test (DAOFactory.getDAOFactory):

		// XUnit Phase 2: Exercise
		DAOFactory daoFactory = DAOFactory.getDAOFactory(DAOFactory.Type.MARIADB);

Lastly, we need to verify that the tested method has actually called the MariaDbDAOFactory constructor. We’ll use PowerMockito.verifyNew for that.
Additionally, we can verify that the object returned by the tested method is indeed the mocked DAO factory we created in the setup phase. A simple JUnit assertEquals will suffice.
The complete test class will look like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DAOFactory.class)
public class DAOFactoryTest {

	@Test
	public void testGetDAOFactoryMariaDb_shouldCreateAndReturnMariaDbDAOFactory() throws Exception {

		// XUnit Phase 1: Setup
		MariaDbDAOFactory mockedDAOFactory = PowerMockito.mock(MariaDbDAOFactory.class);

		PowerMockito.whenNew(MariaDbDAOFactory.class)
					.withNoArguments()
					.thenReturn(mockedDAOFactory);

		// XUnit Phase 2: Exercise
		DAOFactory daoFactory = DAOFactory.getDAOFactory(DAOFactory.Type.MARIADB);

		// XUnit Phase 3: Verification
		PowerMockito.verifyNew(MariaDbDAOFactory.class).withNoArguments();
		Assert.assertEquals(mockedDAOFactory, daoFactory);
	}
}

Final remarks

In this article we’ve learnt how to mock a constructor with the framework PowerMock using a real-life but admittedly simple example. The reasons behind this choice are purely educational. In your day-to-day work you may judge a factory method too simple and well-established to be worth testing. Or you may prefer some integration testing (instead of unit testing) for this method, avoiding any mocking at all.
Secondly, I’ve been using the term mock object instead of test double, which, although widely used, would be consider incorrect by purists. I recommend checking this site or its associated book for more accurate definitions.
Lastly, don’t hesitate in using the comments section to share your thoughts with the rest of us.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s