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.
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.
Consider a class BlockUtils with the following two methods, both of them are static.
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;
}
@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:
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.
We want to test convertBlocksToDescriptors method, not convertBlockToDescriptor. Therefore we will mock the last in order to control its output.
*Both blockOne and blockTwo were defined in the variables stage.
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:
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.