LOGIN

A man sitting in a wooden chair reading a book.

JUnit Test Method Names vs. @DisplayName Annotation

Feb 02, 2024

In software development, writing effective tests is crucial for ensuring the robustness and reliability of your code. When it comes to JUnit, a widely used testing framework in the Java ecosystem, the way you name your test methods can significantly impact the readability and maintainability of your test suite. In this article, we will explore different approaches to naming JUnit test methods, and compare them with the use of the @DisplayNameannotation, shedding light on their respective advantages and considerations.

Applying the core principle of using clear and descriptive names, I will illustrate this concept by building a practical test suite for the widely used ArrayList in Java. The focus will be on crafting test methods that encompass essential functionalities, offering a hands-on exploration of the naming approaches discussed in this article.

Let´s focus on the following functionalities of the ArrayList:

  1. When the list is created, it should be empty.
  2. When an item is added, it should be not empty.
  3. The added item is available on index 0.

Traditional Method Names: The Feature Under Test

A straightforward method of naming a test method involves simply describing the feature under test. In the context of our ArrayList test suite, this results in method names like:

public class ArrayListTest {
    @Test
    public void testNewList() {}

    @Test
    public void testAddItem() {}
}

However, this approach has a significant drawback – the method names communicate the functionality being tested but lack information about the expected result. Furthermore, the testAddItem() method includes multiple verification conditions, such as checking if the list is not empty and whether the added item is present at index 0.

While best practice suggests creating separate test methods for each condition, this often results in different names testAddItem1() and testAddItem2(). A more effective strategy involves including the expected result directly into the method name as shown in the following section.

The Given-When-Then Pattern

A popular approach for naming test methods is the given-when-then pattern to explicitly communicate the object's state under test (given), the performed functionality (when) and the expected result (then). Typically, these parts are separated by underscores, which improves the readability and structure of the test suit.

class ArrayListTest {
    @Test
    void aList_whenCreated_isEmpty() {}

    @Test
    void emptyList_itemAdded_isNotEmpty() {}

    @Test
    void emptyList_itemAdded_itemAvailableOnIndex0() {}
}

This approach allows for various variations, altering the order of given-when-then parts and including additional structural keywords. An example is the when-expect pattern, resulting in slightly shorter method names.

class ArrayListTest {
    @Test
    void when_newList_expect_isEmpty() {}

    @Test
    void when_addItem_expect_notEmpty() {}

    @Test
    void when_addItem_expect_itemAtIndex0() {}
}

For a more natural and expressive tone, longer but descriptive method names can be used, describing the state, precondition, and postcondition.

class ArrayListTest {
    @Test
    void whenAListIsCreatedItShouldBeEmpty() {}

    @Test
    void whenAddingAnItemToAnEmptyListItShouldNotBeEmpty() {}

    @Test
    void whenAddingAnItemToAnEmptyListTheItemShouldBeAvailableOnIndex0() {}
}

While the method names in this structured form are highly expressive, a complete natural sentence is easier to read despite these improvements.

The @DisplayName annotation

JUnit 5 introduced the @DisplayName annotation, providing developers with a practical tool to customize display names in test methods, classes, and nested classes. This annotation's flexibility allows for the inclusion of spaces, special characters, and even emojis in test names, presenting a versatile approach to enhance test clarity and readability.

While some developers choose to combine the @DisplayName annotation with long method names following the given-when-then pattern, I prefer compact method names to avoid unnecessary duplication and potential inconsistency.


@DisplayName("An ArrayList")
class ArrayListTest {
    @Nested
    @DisplayName("after instantiation")
    class NewInstance {
        @Test
        @DisplayName("is empty")
        void empty() {}
    }

    @Nested
    @DisplayName("after adding an item")
    class ItemAdded {
        @Test
        @DisplayName("is not empty")
        void notEmpty() {}

        @Test
        @DisplayName("contains the item at index 0")
        void itemAtIndex0() {}
    }
}

In the provided example, a hierarchy is established where each test name contains its own name along with the names of its parent structures. This hierarchical structure results in clear, natural-sentence-like descriptions of test scenarios and their expected outcomes:

Additionally, this approach offers the advantage that modern IDEs can display the values of the @DisplayName annotations in the corresponding test reports:

The test results as shown by IntelliJ IDEA

Conclusion

The @DisplayName annotation in JUnit provides advantages over regular method names by offering improved readability, enhanced test reports, and structured test naming. By allowing the use of natural language and facilitating the Given-When-Then pattern, @DisplayName creates human-readable and descriptive test names, making it easier for developers to understand the purpose and context of each test. Overall, the annotation enhances documentation within the codebase, promotes consistency across testing frameworks, and serves as a valuable tool for creating informative, well-structured, and maintainable test suites.