junit

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

Advertisements