Testing static methods with PowerMock
Introduction
Writing tdd in JAVA is one of the hottest trends in the past couple years. Every organization wants to have more test coverage for his code and to minimize the chances that a critical bug, a show stopper, will be detected by the customers.
There are several frameworks for writing unit tests in Java, by my favorite is PowerMock. It also supports to other frameworks: easymock and mockito.
powermock has many capabilities and it allow to do almost everything in more than one way.
Today I will focus on how to tests static methods.
Motivation
Test Static methods!? Are you crazy!? Why do we need to test them? They are just “simple” utilities, aren’t they?
Well the above is both right and wrong. Static methods most of the times are just utility methods and not part of the business logic, but this doesn’t mean that we don’t need to test them. Also, every static method can be a member method if static class is turned into some bean.
Use Case
Consider a class BlockUtils with the following two methods, both of them are static.
- convertBlockToDescriptor() convert one class to another. This conversion is expensive since it requires the usage of a SearchEngine.
- convertBlocksToDescriptors() calls convertBlockToDescriptor() several of times. In order not to perform unnecessary calls is save a cache of each conversion and when a conversion is needed it first checks in the cache.
What will we test?
- We will test convertBlocksToDescriptors() method.
- We will test that the caching work – assert the number of times that is convertBlockToDescriptor() called.
- Will mock the usage of SearchEngine since it is out of the test scope and since it is too complicated to use in actual test.
- Will mock the actual conversion that convertBlockToDescriptor() does.
public static BlockDescriptor convertBlockToDescriptor(Block block, SearchEngine searchEngine) {
BlockDescriptor blockDescriptor = new BlockDescriptor(block);
for (Integer blockRecordID : blockDescriptor.getMembers()) {
List<String> recordAttributes = searchEngine.getRecordAttributes(blockRecordID.toString());
blockDescriptor.addTextRecord(blockRecordID, recordAttributes);
}
return blockDescriptor;
}
}
public static List<BlockDescriptor> convertBlocksToDescriptors(List<Block> blocks, SearchEngine searchEngine) {
Map<Block, BlockDescriptor> cache = new HashMap<>();
List<BlockDescriptor> blockDescriptors = new ArrayList<>();
for (Block block : blocks) {
if (cache.containsKey(block)) {
blockDescriptors.add(cache.get(block));
} else {
BlockDescriptor blockDescriptor = BlockUtils.convertBlockToDescriptor(block, searchEngine);
cache.put(block, blockDescriptor);
blockDescriptors.add(blockDescriptor);
}
}
cache.clear();
return blockDescriptors;
}
The Test
@Test
public void testConvertBlocksToDescriptors_cache() throws Exception {
//Variables
Block blockOne = new Block(Arrays.asList(1, 7, 2));
Block blockTwo = new Block(Arrays.asList(8, 10, 2));
SearchEngine searchEngine = PowerMockito.mock(SearchEngine.class);
//Mock Behavior
PowerMockito.mockStatic(BlockUtils.class);
PowerMockito.when(BlockUtils.convertBlocksToDescriptors(Mockito.any(List.class), Mockito.eq(searchEngine))).
thenCallRealMethod();
PowerMockito.when(BlockUtils.convertBlockToDescriptor(Mockito.any(Block.class), Mockito.eq(searchEngine)))
.thenReturn(new BlockDescriptor(blockOne), new BlockDescriptor(blockTwo));
//execute
List<BlockDescriptor> blockDescriptors = BlockUtils.convertBlocksToDescriptors(Arrays.asList(blockOne, blockTwo, blockTwo), searchEngine);
MatcherAssert.assertThat(blockDescriptors, Matchers.hasSize(3));
//verify
PowerMockito.verifyStatic(Mockito.times(2));
BlockUtils.convertBlockToDescriptor(Mockito.any(Block.class), Mockito.eq(searchEngine));
}
Our test is made out of 4 parts:
- Deceleration of Variables
- Setting the Mock Behavior
- Execution of the tests method + assertion
- Verify how many times each method was called
Variables
In this step we declare and initialized all the variables that will be used in the test.
They can be either real instances or Mocks.
In the given test SearchEngine.class is an external resource that doesn’t affect on the internal logic of the test. Also it is a very complicated to generate an instance of it, therefore we create a mock of it that will passed as a parameter.
Mock Behavior
We want to test convertBlocksToDescriptors method, not convertBlockToDescriptor. Therefore we will mock the last in order to control its output.
- On line 8 we create a mock for BlockUtils class.
- On line 9 we define that whenever convertBlocksToDescriptors will be called with any List and with the mocked searchEngine the real method, the one in BlockUtils class, will be called.
- on line 10 we define that on the first invocation of convertBlockToDescriptor it will return a blockDescriptor for blockOne and afterwards on any further call it will return a blockDescriptor for blockTwo.
*Both blockOne and blockTwo were defined in the variables stage.
Execute
We execute convertBlocksToDescriptors method.
We pass it three parameters and test that its output is a list with three parameters as well.
We only assert the size of the output and not its content since:
- Previously we defined the response of convertBlockToDescriptor(). This method is mocked and there is no need to verify the mocked is working properly.
- The output size of convertBlocksToDescriptors() must be equal to the input size.
Verify
This is the Crème de la crème of the test.The goal of the test is to verify that the internal caching of the method is working.
If it does, than convertBlockToDescriptor() will be invoked only twice.
(The third conversion will not be performed and its value will be fetched from the cache).
The verification itself is a two steps action.
- First we define a rule for the number of times a method should be invoked.
This can be a specific number such as 2, or never(), atLeastOnce(), atLeast(2) and atMost(2) - Then we define a method that should be enforced by that rule.
In the above example we defined convertBlockToDescriptor() with any Block and SearchEngine as parameters