Friday 31 May 2013

7 tips to make your tests readable

If you want to make your TDD sustainable, then please give importance to the readability of your tests. When a programmer reads your test, he or she needs to understand the purpose of test. No developer likes to stop and puzzle through a test to figure out what it does. You can reduce the cognitive load of your reader by making your tests readable. Here are some tips that you can use to improve the readability of your tests. I hope you will find them useful.

Tip 1: Give importance to test name

By choosing a right name for your test you are giving the first clue to your reader about the intention your test and how the target object is supposed to behave. Try to select a name that says something about the scenario and the expected behaviour.

Tip 2: Structure your unit test

Try to follow "Arrange, Act, Assert" pattern to structure your unit test. "Arrange, Act, Assert" basically means that you want to organize your tests such that you first arrange the objects used in the test, then trigger the action and make assertions about the outcome last. You can add whitespace in between these three segments to help others to understand your tests more easily. For example:

@Test public void shouldFindCustomerByUsername(){ //Arrange when(customerDaoMock.findByUsername("jonsmi")) .thenReturn(getFakeCustomer()); //Act Customer customer = customerService .findCustomerByUsername("jonsmi"); //Assert assertThat(customer.getId(), is(101L)); assertThat(customer.getUsername(), is("jonsmi")); }

Tip 3: Put emphasize on "what" over "how"

Try to give importance on "what" over "how" even for your test code. Move out the unnecessary implementation details from your test code. This details create noise, which makes harder for your reader to understand what is important in your test. Also try to use Hamcrest matcher utilities such as assertThat, is, anything, notNullValue, hasItem etc. JUnit currently ships with Hamcrest. These utilities help you express your intent clearly and reduce verbosity from your tests. For instance, instead of doing this:

assertTrue(activities.contains("PLAY")); assertTrue(activities.contains("READING")); assertTrue(activities.contains("WRITING"));
You can do this:
assertThat(activities, hasItems("PLAY", "READING", "WRITING"));

Tip 4: Extract common features into methods that can be shared

Many times we write the same thing again and again in our test methods. Remember DRY (Don't Repeat Yourself) principle. Extract common or nonessential features into private helpers and setup methods. But be careful not to make your tests so abstract that future readers do not understand what tests do.

Tip 5: A test should only check one thing and check it well

When you put multiple tests in a single test method, you are going to confuse others. If you have a big test method, split it into smaller test methods. Each test should focus on single fixture. By doing this, you will improve readability significantly. Another additional benefit you will get, when a test fails you need to look into smaller portion of code to find the reason. So it improves maintainability as well.

Tip 6: Try to avoid magic numbers

I think every programmer agrees that magic numbers are bad and should be avoided. Replace them with constants or variables that give them desired meaning, making code easier to read.

Tip 7: Simplify your setup method

Do not dump everything in your setup method (annotated with @Before or @BeforeClass). By doing this, you are making setup method over complicated. It is also an indication of design problem that forces the test to do so much work to put an object under test. Extract all the nonessential details from the setup into private methods. Give appropriate and descriptive names to variables and methods used in setup method.


References:
  1. Growing Object-Oriented Software, Guided By Tests - Steve Freeman, Nat Pryce
  2. Effective Unit Testing - Lasse Koskela
  3. Hamcrest
  4. JUnit
  5. TestNG