JUnit 5 Nested Tests

junit 5 nested test

This article shows you how to use the JUnit 5 @Nested annotation to group tests together.

P.S Tested with JUnit 5.5.2

Why nested tests?
It’s optional to create nested tests. Still, it helps to create hierarchical contexts to structure the related unit tests together; in short, it helps to keep the tests clean and readable.

Let’s see the following example – Tests for a CustomerService.

1. Single Test Class

1.1 By default, we may create all the tests in a single class like this:

CustomerServiceMethodTest.java

package com.mkyong.nested.samples;

import com.mkyong.customer.service.CustomerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("Test Customer Service")
public class CustomerServiceMethodTest {

    CustomerService customerService;

    @BeforeEach
    void createNewObjectForAll() {
        System.out.println("New CustomerService()");
        //customerService = new CustomerServiceJDBC();
    }

    @Test
    void findOne_with_id() {
        //customerService.findOneById(2L);
    }

    @Test
    void findOne_with_name() {
        //customerService.findOneByName(2L);
    }

    @Test
    void findOne_with_name_regex() {
        //customerService.findOneByNameRegex("%s");
    }

    @Test
    void findAll_with_ids() {
        //customerService.findAllByIds(Arrays.asList(2, 3, 4));
    }

    @Test
    void findAll_with_name_like() {
        //customerService.findAllByName("mkyong");
    }

    @Test
    void update_with_new() {
        //customerService.update(new Customer());
    }

    @Test
    void update_with_existing() {
        //customerService.update(new Customer());
    }

}

Output in IDE.

tests in ide

If the CustomerService is adding more features, this single test class will overloads easily with hundreds of test methods. In the end, we create a messy, significant, and unorganized single test class.

2. Tests by Classes

2.1 Some developers started to group the related testes by class name like this:

CustomerServiceFindOneTest.java

package com.mkyong.nested.samples;

import com.mkyong.customer.service.CustomerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class CustomerServiceFindOneTest {

    CustomerService customerService;

    @BeforeEach
    void createNewObjectForAll() {
        System.out.println("New CustomerService()");
        //customerService = new CustomerServiceJDBC();
    }

    @Test
    void findOne_with_id() {
        //customerService.findOneById(2L);
    }

    @Test
    void findOne_with_name() {
        //customerService.findOneByName(2L);
    }

    @Test
    void findOne_with_name_regex() {
        //customerService.findOneByNameRegex("%s");
    }

}
CustomerServiceFindAllTest.java

package com.mkyong.nested.samples;

import com.mkyong.customer.service.CustomerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class CustomerServiceFindAllTest {

    CustomerService customerService;

    @BeforeEach
    void createNewObjectForAll() {
        System.out.println("New CustomerService()");
        //customerService = new CustomerServiceJDBC();
    }

    @Test
    void findAll_with_ids() {
        //customerService.findAllByIds(Arrays.asList(2, 3, 4));
    }

    @Test
    void findAll_with_name_likeY() {
        //customerService.findAllByName("mkyong");
    }
    
}
CustomerServiceUpdateTest.java

package com.mkyong.nested.samples;

import com.mkyong.customer.service.CustomerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class CustomerServiceUpdateTest {

    CustomerService customerService;

    @BeforeEach
    void createNewObjectForAll() {
        System.out.println("New CustomerService()");
        //customerService = new CustomerServiceJDBC();
    }

    @Test
    void update_with_new() {
        //customerService.update(new Customer());
    }

    @Test
    void update_with_existing() {
        //customerService.update(new Customer());
    }

}

If the CustomerService is adding more features, for example, new 30+ methods, are we going to create 30+ test classes for a CustomerService? How to run the 30+ test classes, by pattern or create a new test suite?

3. Nested Tests

3.1 For big class, we should consider the @Nested tests, all tests in a single test class (in a hierarchical structure), and the hierarchical output in IDE makes the tests more readable.

Furthermore, we can also initialize an object and reuse for all the nested tests.

CustomerServiceNestedTest.java

package com.mkyong.nested;

import com.mkyong.customer.service.CustomerService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

@DisplayName("Test Customer Service")
public class CustomerServiceNestedTest {

    CustomerService customerService;

    // Create one customerService object and reuse for all the nested tests
    @Test
    @DisplayName("new CustomerService() for all the nested methods.")
    void createNewObjectForAll() {
        System.out.println("New CustomerService()");
        //customerService = new CustomerServiceJDBC();
    }

    @Nested
    @DisplayName("findOne methods")
    class FindOne {
        @Test
        void findOne_with_id() {
            //customerService.findOneById(2L);
        }

        @Test
        void findWith_with_name() {
            //customerService.findOneByName(2L);
        }

        @Test
        void findWith_with_name_regex() {
            //customerService.findOneByNameRegex("%s");
        }
    }

    @Nested
    @DisplayName("findAll methods")
    class FindAll {
        @Test
        void findAll_with_ids() {
            //customerService.findAllByIds(Arrays.asList(2, 3, 4));
        }

        @Test
        void findAll_with_name_likeY() {
            //customerService.findAllByName("mkyong");
        }
    }

    @Nested
    @DisplayName("update methods")
    class Update {
        @Test
        void update_with_new() {
            //customerService.update(new Customer());
        }

        @Test
        void update_with_existing() {
            //customerService.update(new Customer());
        }
    }

}

Output in IDE.

nested tests in ide

Download Source Code

$ git clone https://github.com/mkyong/junit-examples
$ cd junit5-examples
$ check src/test/java/com/mkyong/nested/*.java

References

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

avatar