Simple steps to make your JUnit tests better (part 1)

One of my latest tasks was to review and refactor unit tests in our system, which I found especially suitable for me. After presenting my results to the team guys asked me to create a page on Confluence describing my remarks and proposed practices we should follow – what a great occasion for a blog post. So here I present some practices that in my opinion (and my team’s opinion too) can improve your tests and make them cleaner, simpler and more readable.

Proper test method naming

The name of the test method is as important as the test itself. The key is that it should describe the behaviour that is tested – not the name of the method that is tested, not all the arguments that are taken, but the behaviour. So names like testMethodA are absolutely wrong and please never use such a construct. Also have in mind that such naming lead to monsters like testMethodA2, then 3, 4, 64, 382 and so on leaving the code in a state that nobody knows what is actually tested and where. If naming was done well you don’t even need to look inside the test method to understand what’s going on and what is actually tested. To achieve it use on of BDD naming conventions which enforces you to think about the behaviour that it under test before you start coding and describe it well. Personally my favourite naming convention is “should do something when something” In example:

@Test
public void shouldPopulateAuthorDataWhenGeneratingReport()

This is a powerful practice for beginners as starting the test method with the magic “should” word makes you literally stop and ask your self “Yeah, what it actually should do?”.

Don’t be afraid of long method names – we’re not having any limits on that so you can put inside method name everything that you consider essential to fully express what’s going on under the hood. Test are for developers and developers should read camel case text as fast as normal one so argument that long names are unreadable for me is invalid. However, if you method name is getting longer and longer check if the test isn’t doing too much and if you design is fine.

 Use matchers instead of simple asserts

Using matchers library will make working with test much simpler because of two reasons. First, tests are more readable. Your assertions are starting to tell a story describing the behaviour. To give an example look at these two assertion below and for full effect try to read them aloud.

assertTrue(!(shoppingList.size() > 2));
assertThat(shoppingList, hasSize(not(greaterThan(2))));

Hope you got the point. I have seen lots of tests with all assertions done with assertTrue only and this is why I used it here. Even though using hamcrest gives you a bit more code, you gain on readability – you don’t have to analyze carefully what are the conditions, it’s given on a silver platter. Here the assertion is quite simple, but imagine analysis on more complicated cases.

Secondly, matchers simplify debugging a lot providing elegant logging when assertions fail. Let’s look at logs from both failures of assertion given in the code above. When using first assertion all we get is

java.lang.AssertionError

which literally tells nothing and we have no information what went wrong. On the other hand using matchers we get quite informative log giving us a first place to start analysis.

java.lang.AssertionError:
Expected: a collection with size not a value greater than <2>
but: collection size was <3>

 Write concise tests

You should keep inside the test method only things that are essential to describe tested behaviour, extract everything else to increase readability and the ease of understanding what is going on inside the test. Take a look at this example.

	@Test
	public void shouldDetrminePaperAsPackingMaterialIfItemIsRound() {
		Item item = new Item();
		item.setWidth(10);
		item.setHeight(20);
		item.setColor(RED);
		item.setShape(ROUND);
		
		Material material = packingAdvisor.determinePackingMaterial(item);
		
		assertThat(material, is(instanceOf(PAPER)));
	}

Do we need all the details of an item inside the test when the only thing that matters is it’s size? This is nothing but a noise in the code leading to more time spent on understanding it. Person looking at this test will try to analyze all the fields, thinking that possibly the values have some kind of influence on the test result. Item’s size and color are values that are not tested and should be extracted to separate method used to construct items – how to do it well is a topic for another blog post of course.

Setting up all items fields in each test case is causing also code duplication which is one of basic faults.

To be continued.