Tests should describe a linear story, as opposed to production code. What things we start with, what we do with them, and what we get. Some call this the Given/When/Then pattern, others the Arrange/Act/Assert pattern. This means that the code should be written as a single thread that can be read with as little mental effort as possible. Although it might seem obvious in simple cases, it also means that, for example, you should avoid loops when initializing your test data:
public void can_limit_search_results_to_5() { // I recommend this over a for-loop List list = new ArrayList(); list.add("string"); list.add("string"); list.add("string"); list.add("string"); list.add("string"); list.add("string"); // 6 Store store = new Store(list); assertThat(store.findByName("string")).hasSize(5); }
Similarly, avoid try/catch blocks. In older versions of JUnit, in order to check that an exception was, you had to write code like this:
public void can_fail_when_searching_on_an_invalid_name() { try { search.findByName("an invalid name"); fail(); } catch (RuntimeException e) { // success } }
Since JUnit 4.0, this can be refactored as: @Test(expected = RuntimeException.class) public void can_limit_search_results_to_ten() { search.findByName(“an invalid name”); } This has the added benefit of producing a more explicit error message from JUnit when the test fails. What if you need to check the content of the exception message? JUnit 4 does offer a mechanism for this, but I find it rather cumbersome. I prefer the catch exception library:
public void can_fail_when_searching_on_an_invalid_name() { catchException(search).findByName("an invalid name"); assertThat(caughtException()).isInstanceOf(RuntimeException.class); assertThat(caughtException()).hasMessage("Name is invalid"); }
If you understand French and want to hear more about the Catch Exception library, you might be interesting in this podcast episode I recently published. This is an excerpt of a chapter I’m writing for the upcoming CITCON Chronicles book.