Unit Testing

Adventures with PowerMock

Recently I had the opportunity/challenge/task to get unit tests working for some legacy code. Various tests were written, but very few worked in our automated build environment. I spent the time to get the tests working. The tests were a hodgepodge of unit and integration tests and probably orginally only worked locally on one person’s machine. I converted the current set of tests into unit tests (maybe I’ll get to integration tests some day) that could be run through Eclipse, through Ant and through our automated build environment. Turned out, I used Mockito and PowerMockito quite a bit. Here are some of my lessons learned.

Mocking the Statics

A bunch of the legacy code classes are similar to this:

public class MyClass {

	public MyClass(MyClassTO to) {
		super(to);
	}

	public void create() {
		MyDAO dao = getDAO();
		List list = dao.find(/*params here*/);
		// do something
	}
	
	public static void delete(Object pk) {
		getDAO().delete(pk);
		// do something
	}

	private static MyDAO getDAO() throws DAOException {
        	return (MyDAO) DAOFactory.getDAO(MyDAO.class);
    }
}

And, I wanted to unit test any of the methods that are part of the class that were worth testing. The challenge was the “private static getDAO()” method. DAOFactory needs to be run in the container to work. So, my first thought was “I want to mock the getDAO() method and have it return a mock dao.” I tried a ton of things to follow through with this idea with zero results. Turns out, it was better to think of my DAOFactory as a System Class. And then these directions https://code.google.com/p/powermock/wiki/MockSystem worked very well.

My test class looks like:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ DAOFactory.class })
public class MyClassTest {

	private MyDAO mockDao;
	private MyClassTO to;

	@Before
	public void setUp() throws Exception {
		to = new MyClassTO(/*params here*/);
		to.setDescription("description");

		mockDao = mock(MessageDAO.class);
		PowerMockito.mockStatic(DAOFactory.class);
		PowerMockito.when(DAOFactory.class, "getDAO", MyDAO.class).thenReturn(mockDao);
	}

	@Test
	public void testCreateCreate() {
		List<MyClassTO> myList = new ArrayList<MyClassTO>();
		Mockito.when(mockDao.find(/*params here*/)).thenReturn(myList);

		MyClass bo = new MyClass(to);
		bo.create();
		verify(mockDao).create(bo);
		verify(mockDao, never()).update(bo);
	}
}

By saying, mock the DAOFactory and make it return my mockDao, I can then mock out whatever else is needed to test the class.

Preventing an Instantiation
Next pattern I had to handle was when my class under test instantiated an object that eventually needed something in the container. Usually that DAOFactory. In this case, my class under test looked something like this (much simplified).:

public final class MyDelegate  {
    private MyDelegate() { }

    public static void doSomeStuff(MyTO to) {
		MyMappingBO bo = new MyMappingBO();
		List mappings = bo.find(to);
		bo.save();
    }
}

I needed to not instantate the MyMappingBO class during the test. This is what I did using PowerMockito.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ MyDelegate.class })
public class MyDelegateTest {

    @Test
    public void testCreate() throws Exception {
        List<MyTO> myList = new ArrayList<MyTO>();
        // populate the list
       
        MyTO to = new MyTO();
        MyMappingBO mockMyMappingBo = mock(MyMappingBO.class);
        PowerMockito.whenNew(MyMappingBO.class).withNoArguments().thenReturn(mockMyMappingBo);
        PowerMockito.when(mockUserMappingBo.find(to)).thenReturn(myList);
		
        MyDelegate.doSomeStuff(to);
        //asserts and verifies here
    }
}

It does what it reads. When the MyMappingBO class is newed up, return the mock instead. Then, when a specific method on the mock is called, return the myList object instead. The tricky thing here is which class to put in the @PrepareForTest. Actually, if you read the PowerMock documentation closely, it says it pretty clearly. It is the class instantiates the object you are mocking, not the mocked class itself.

Version Info:
JUnit 4.11
Mockito 1.9.5
PowerMock 1.5

Spring 3 @Autowired Unit Tests with Mockito

I’ve been wanting to enhance the unit tests I have in my application for awhile. I’m using Spring 3 testing facilities, but most of my tests are “integration” tests. I finally carved out a bit of time to enable better unit tests in my application. This is an example of a unit test of a simple service class using Spring 3 annotations. I am using Mockito to mock the dependencies.

First my service class. Like I said, simple. Just two methods and straightforward logic.

package my.app.service;

import java.util.List;

import my.app.dao.SupervisorDAO;
import my.app.model.Supervisor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SupervisorService {

	@Autowired private SupervisorDAO supervisorDao;

	@Transactional(readOnly=true)
	public List getSupervisors() {
		return supervisorDao.getSupervisors();
	}

	/**
	 * Determine if the supervisor is responsible for any users for the
	 * specified department
	 * @param dbId The dbId of the supervisor
	 * @param department The department to look for
	 * @return true if there are users assigned to the superivsor for the department
	 */
	public boolean isSupervisorResponsibleForUser(int dbId, String department) {
		if (dbId == 0) {
			throw new IllegalArgumentException("A dbId is required");
		}
		if (department == null) {
			throw new IllegalArgumentException("department is required");
		}
		boolean hasUsers = false;
		int numAssignedUsers = supervisorDao.getUserCountForSupervisor(dbId, department);
		if (numAssignedUsers > 0) {
			hasUsers = true;
		}
		return hasUsers;
	}
}

The unit test. I need to mock the dao object to create a repeatable unit test of the service class. Here’s the code:

package my.app.service;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import my.app.dao.SupervisorDAO;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class SupervisorServiceUnitTest {

	@Mock private SupervisorDAO mockSupervisorDao;
	@InjectMocks private SupervisorService supervisorService;

	@Before
	public void init() {
		when(mockSupervisorDao.getUserCountForSupervisor(1, "Department1"))
			.thenReturn(2);
		when(mockSupervisorDao.getUserCountForSupervisor(1, "Department2"))
			.thenReturn(0);
	}

	@Test
	public void testIsSupervisorResponsibleForUserTrue() {
		boolean isSupervisor = supervisorService.isSupervisorResponsibleForUser(1, "Department1");
		assertTrue(isSupervisor);
	}

	@Test
	public void testIsSupervisorResponsibleForUserFalse() {
		boolean isSupervisor = supervisorService.isSupervisorResponsibleForUser(1, "Department2");
		assertFalse(isSupervisor);
	}

	@Test(expected=IllegalArgumentException.class)
	public void testIsSupervisorResponsibleRequiredDbId() {
		supervisorService.isSupervisorResponsibleForUser(0, "Department3");
	}

	@Test(expected=IllegalArgumentException.class)
	public void testIsSupervisorResponsibleRequiredDivision() {
		supervisorService.isSupervisorResponsibleForUser(1, null);
	}
}

The key things that I learned along the way:

  • Use @RunWith(MockitoJUnitRunner.class) to run this test
  • Use the @Mock annotation for the classes that need to be mocked within the class you are testing. Use the @InjectMocks annotation on the class you are testing. The @Mock classes are injected into the @InjectMocks class. I guess this should have been obvious, but it wasn’t to me.

It’s a simple class and a simple unit test, but it’s a start. It gets me over the hump of having the correct jars and knowing how to get a test up and running. I am new to the behavior driven style of Mockito, but this works well for me in this unit test. I think it is clean and easy to read.

Some links that were helpful to me (disclaimer, some of these are for older versions of Mockito)

Version Info:

  • Spring 3.0.5
  • Mockito 1.9.0