JUnit 5 Nested Tests
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:
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.
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:
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");
}
}
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");
}
}
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.
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.
Download Source Code
$ cd junit5-examples
$ check src/test/java/com/mkyong/nested/*.java