Overview

(J)Unit Testing Principles

No Comments

This article is about basic principles for Java JUnit tests. Even though most of the following principles are probably also true for unit tests written in other programming languages.

Every now and then I am really wondering why we – the folks developing software – have so much trouble agreeing on how to do certain things. Now unit testing is really around long enough that one would believe there are no fundamental discussions on the dos and don’ts. But those discussions are there – constantly :)! Therefore I will try to give some reasonable principles for JUnit Testing in the following to maybe mitigate at least some of the discussion points.

JUnit tests must not make use of any infrastructure

At some point of time this seems to happen in every project: Tests are written that require a running database system or any other piece of the infrastructure the application is running on. But databases are really a kind of favourite here.

If you feel the urgent need to write these kind of tests, just grab a cup of coffee, relax and consider mocking for the database access in your unit tests!

If testing on the physical database layer is required, Integration Tests are probably the way to go. But those are then only executed on specific environments or locally if wanted, but not as part of the normal JUnit cycle. One very important aspect of executing JUnit tests is speed!

“If people don’t stick to the plan, then this leads to chaos, and no-one likes chaos” – Parker

Personally I consider these tests most of the time completely useless and prefer testing this implicitly when testing features. This is then done preferably using Automated Acceptance Tests or with traditional QA.

Test the public API of your application

The first topic might go with little discussion – if you are lucky. But this one will not. Just googling for this will bring up endless discussion whether or not private methods should be tested explicitly or implicitly through the public API.

Make everyones life easier and only write JUnit tests agains the public API of your application.

There cannot be any private methods that are not anyway executed through the public interfaces unless we are considering really esoteric cases. Therefore all private methods are anyway tested implicitly when testing the corresponding public API.

Testing private methods directly does not only require some technical wrinkle, but it also makes the tests more susceptible to refactorings done in the code under test. And the public interface provides the business logic of your application and this is what we want to test. A more in-depth view on this specific topic can be found from this excellent article.

Test classes of the application in isolation

Considering the previous point a JUnit test should test the public API of one class in isolation. This means all services/methods used from other classes must be mocked. This does of course exclude data transfer objects and other POJOs.

Unit tests are isolated and on class-level.

We are having one “test-class” that corresponds to the class we are testing and we are having one or more “test-methods” for each public method in that class. Really straightforward and well supported by IDEs.

Test methods are as small as possible and well structured

One test-method should test one specific behavior of your API. If you need to test behavior in error situations write an own test-method for it. If you need to test certain boundary cases then write own test-methods for each of those. The advantage of this approach is not only that the test code will be much more readable, but in case of a failing test it is immediately possible to locate the problem.

Break down the test-methods into preparation, execution and verification. Or simply Given/When/Then :-). The following code-snippet shows an example.

@Test
public void getItemNameUpperCase() {
 
    //
    // Given
    //
    Item mockedItem = new Item("it1", "Item 1", "This is item 1", 2000, true);
    when(itemRepository.findById("it1")).thenReturn(mockedItem);
 
    //
    // When
    //
    String result = itemService.getItemNameUpperCase("it1");
 
    //
    // Then
    //
    verify(itemRepository, times(1)).findById("it1");
    assertThat(result, is("ITEM 1"));
}

Especially if more complex functionality is tested where more preparation – probably through mocking – and more verification is required this kind of source code documentation is really helpful. Agree on a common style for this inside the project team.

Following these basic principles for the implementation of JUnit tests should already help a lot in daily project work. Of course when pairing is used or some review process for the feature development the same should be applied when writing JUnit tests.

Comment

Your email address will not be published. Required fields are marked *