JUnit 5: Writing Nested Tests

Credits

Introduction to Our Test Class

The previous part of this tutorial described how we can use setup and teardown methods, and add test methods to our test classes. Also, we wrote a simple test class and added all setup and teardown methods to the created class.

The source code of our test class looks as follows:

import org.junit.jupiter.api.*;
 
@DisplayName("JUnit 5 Nested Example")
class JUnit5NestedExampleTest {

@BeforeAll
static void beforeAll() {
    System.out.println("Before all test methods");
}

@BeforeEach
void beforeEach() {
    System.out.println("Before each test method");
}

@AfterEach
void afterEach() {
    System.out.println("After each test method");
}

@AfterAll
static void afterAll() {
    System.out.println("After all test methods");
}

}

Next, we will add nested setup, teardown, and test methods to our test class.

Writing Nested Tests

When we write nested tests with JUnit 5, we have to create a nested test class hierarchy that contains our setup, teardown, and test methods. When we add nested test classes to our test class, we have to follow these rules:

  • All nested test classes must be non-static inner classes.
  • We have to annotate our nested test classes with the @Nested annotation. This annotation ensures that JUnit 5 recognizes our nested test classes.
  • There is no limit for the depth of the class hierarchy.
  • By default, a nested test class can contain test methods, one @BeforeEach method, and one @AfterEach method.
  • Because Java doesn’t allow static members in inner classes, the @BeforeAll and @AfterAll methods don’t work by default.

There are two things I want to point out:

  • We should annotate our nested test classes with the @DisplayName annotation because it allows us to replace technical names with a sentence that describes the purpose of the nested test class.
  • If you want to use the @BeforeAll and @AfterAll methods in nested test classes, you should take a look at the JUnit 5 User Guide.

Let’s add a few inner classes to our test class. The idea of this exercise is to demonstrate the invocation order of setup, teardown, and test methods. We can add the required inner classes to our test class by following these steps:

First, we have to add a new inner class called A to our test class and annotate the inner class with the @Nested annotation. After we have created the A class, we have to add one setup, teardown, and test method to the created inner class.

After we have added this inner class to the JUnit5NestedExampleTest class, the source code of our test class looks as follows:

import org.junit.jupiter.api.*;
 
@DisplayName("JUnit 5 Nested Example")
class JUnit5NestedExampleTest {

@BeforeAll
static void beforeAll() {
    System.out.println("Before all test methods");
}

@BeforeEach
void beforeEach() {
    System.out.println("Before each test method");
}

@AfterEach
void afterEach() {
    System.out.println("After each test method");
}

@AfterAll
static void afterAll() {
    System.out.println("After all test methods");
}

@Nested
@DisplayName("Tests for the method A")
class A {

    @BeforeEach
    void beforeEach() {
        System.out.println("Before each test method of the A class");
    }

    @AfterEach
    void afterEach() {
        System.out.println("After each test method of the A class");
    }

    @Test
    @DisplayName("Example test for method A")
    void sampleTestForMethodA() {
        System.out.println("Example test for method A");
    }
}

}

Second, we have to add a new inner class called WhenX to the A class and annotate the inner class with the @Nested annotation. After we have created the WhenX class, we have to add one setup, teardown, and test method to the created inner class.

After we have added this inner class to the A class, the source code of our test class looks as follows:

import org.junit.jupiter.api.*;
 
@DisplayName("JUnit 5 Nested Example")
class JUnit5NestedExampleTest {

@BeforeAll
static void beforeAll() {
    System.out.println("Before all test methods");
}

@BeforeEach
void beforeEach() {
    System.out.println("Before each test method");
}

@AfterEach
void afterEach() {
    System.out.println("After each test method");
}

@AfterAll
static void afterAll() {
    System.out.println("After all test methods");
}

@Nested
@DisplayName("Tests for the method A")
class A {

    @BeforeEach
    void beforeEach() {
        System.out.println("Before each test method of the A class");
    }

    @AfterEach
    void afterEach() {
        System.out.println("After each test method of the A class");
    }

    @Test
    @DisplayName("Example test for method A")
    void sampleTestForMethodA() {
        System.out.println("Example test for method A");
    }

    @Nested
    @DisplayName("When X is true")
    class WhenX {

        @BeforeEach
        void beforeEach() {
            System.out.println("Before each test method of the WhenX class");
        }

        @AfterEach
        void afterEach() {
            System.out.println("After each test method of the WhenX class");
        }

        @Test
        @DisplayName("Example test for method A when X is true")
        void sampleTestForMethodAWhenX() {
            System.out.println("Example test for method A when X is true");
        }
    }
}

When I write nested tests for my code, I use the following class hierarchy:

  • The “root” test class contains all test methods of the system under test. Typically I name this class by appending the string: Test to a string that identifies the system under test.
    • An inner class that contains the all tests of a feature or a method. I use the name of the tested feature or method as the name of this inner class.
      • The inner classes which which verify that the system under test is working as expected when a specific condition is true. I name these inner classes by prepending the string: When to a string that describes the condition. For example, if we are writing tests for a finder method, we could have two inner classes: WhenXIsFound and WhenXIsNotFound.

Additional Reading:

We have now written a test class that contains nested tests. Let’s see what happens when we run our tests.

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments