PowerMock and Static Method Stubbing
Up till now I have not come across that many projects that would truly conform to the principles of Test Driven Development. Once again, I found myself in an unenviable situation of introducing unit tests to a code which had been released to production a long time ago, completely untouched by any kind of test automation. My aim was to keep the tests simple, understandable and fast so that one could easily add more until the core business logic gets a sufficient test coverage without delaying the build time. Due to a high number of intrinsic dependencies it was a challenge to keep the tests on a unit level and not to resort to complex (and potentially much slower) integration tests. I decided to mock out most of the dependencies and stay focused to the tested classes only. Mockito helped me a great deal, though there were some pitfalls along the way. Going about static method calls was one of them. Today, I would like to a share my experience with PowerMock and static method stubbing.
One of the tested methods produced a sorted list of values. Sorting was an essential part of the business logic. Here is a quick draft of how the implementation looked like:
class Foo { public List<String> getTheList() {..} } class Bar { private Foo foo = new Foo(); public List<String> doSomethingClever() { List<String> theList = foo.getTheList(); .. // do something clever .. // finally, return the sorted results Collections.sort(theList) return theList; } }
With simplicity in mind I didn’t bother with looping over the list to see if the records were sorted as expected. Instead, I merely wanted to check that the list was passed as an argument to the static call of Collections.sort. Using PowerMock it only takes a few lines of code to accomplish this task:
.. import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.verifyStatic; .. @Test public void recordsShouldBeSorted() { // the list I expect to be sorted later on final List expectedList = new Foo().getTheList(); // letting PowerMock in spy(Collections.class); doNothing().when(Collections.class); // confusing as it is, no sorting happens at this stage Collections.sort(expectedList) // cool, now I am ready to check the list got sorted // without looking at the returned data // let's invoke the tested method new Bar().doSomethingClever(); // time to verify the sorting was applied verifyStatic(); // once again a bit confusing // this time it checks the expected list // was passed as an argument to the sort call Collections.sort(expectedList); }
Hope it makes sense. If not, here is the pattern from the framework’s point of view:
- Tell me which class and which static method
- Call the business method invoking the static method we agreed on
- Let me check if the static method got called with expected arguments
import org.junit.Test;import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({Collections.class, Bar.class}) public void BarTest { @Test public void recordsShouldBeSorted() {..}
That’s it. Originally, I overlooked the need to add the tested class (Bar in this example) to the PrepareForTest annotation. I was pulling my hair out when trying to understand why the heck the test wouldn’t pass even though debugging proved the sorting did happen. It took me way too long to spot that tiny but substantial mistake. I hope this post helps someone else to save a bit of their time.