Combining @Nested classes with @ParameterizedTest enables data-driven testing within organized hierarchical structures, allowing parameterized test methods to share context and lifecycle across related test scenarios.
Parameterized Nested Benefits
This approach allows you to test multiple data variations while maintaining the organizational benefits of nested classes, creating clean separation between different test contexts with their own parameter sets.
Example
src/test/resources/test-data.csvinput,expected
1,2
2,4
3,6
5,10
10,20
package com.logicbig.example;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
public class NestedWithParameterizedTestsTest {
private String systemName;
@BeforeEach
void setUpSystem() {
systemName = "TestSystem";
}
@Test
void systemInitializationTest() {
assertEquals("TestSystem", systemName);
}
@Nested
class MathOperations {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void squareTest(int number) {
int result = number * number;
assertTrue(result > 0);
assertEquals(Math.pow(number, 2), result, 0.001);
}
@ParameterizedTest
@CsvSource({"1, 2, 3", "5, 10, 15", "-3, 3, 0"})
void additionTest(int a, int b, int expected) {
assertEquals(expected, a + b);
}
@Nested
class AdvancedMath {
@ParameterizedTest
@MethodSource("powerDataProvider")
void powerTest(int base, int exponent, double expected) {
assertEquals(expected, Math.pow(base, exponent), 0.001);
}
static Stream<Arguments> powerDataProvider() {
return Stream.of(
Arguments.of(2, 3, 8.0),
Arguments.of(5, 2, 25.0),
Arguments.of(10, 0, 1.0)
);
}
@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv", numLinesToSkip = 1)
void csvFileTest(int input, int expected) {
assertEquals(expected, input * 2);
}
}
}
@Nested
class StringOperations {
private String prefix = "TEST_";
@BeforeEach
void setUpStringContext() {
prefix = "TEST_";
}
@ParameterizedTest
@ValueSource(strings = {"hello", "world", "junit", "testing"})
void stringLengthTest(String input) {
assertTrue(input.length() > 0);
assertEquals(input.length(), input.length());
}
@ParameterizedTest
@CsvSource({"hello, HELLO", "jUnit, JUNIT", "test, TEST"})
void uppercaseTest(String input, String expected) {
assertEquals(expected, input.toUpperCase());
}
@ParameterizedTest
@MethodSource("prefixedStringProvider")
void prefixedStringTest(String input, String expected) {
String result = prefix + input;
assertEquals(expected, result);
}
static Stream<Arguments> prefixedStringProvider() {
return Stream.of(
Arguments.of("data", "TEST_data"),
Arguments.of("value", "TEST_value"),
Arguments.of("input", "TEST_input")
);
}
@Nested
class StringValidation {
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {" ", "\t", "\n"})
void blankStringTest(String input) {
assertTrue(input == null || input.trim().isEmpty());
}
@ParameterizedTest
@EnumSource(value = TestEnum.class, names = {"ACTIVE", "PENDING"})
void enumTest(TestEnum status) {
assertNotNull(status);
assertTrue(status == TestEnum.ACTIVE || status == TestEnum.PENDING);
}
}
}
@Nested
class DataCombinationTests {
@ParameterizedTest
@CsvSource({"2, true", "3, false", "4, true", "5, false"})
void evenOddTest(int number, boolean isEven) {
if (isEven) {
assertEquals(0, number % 2);
} else {
assertEquals(1, number % 2);
}
}
@ParameterizedTest
@MethodSource("rangeDataProvider")
void rangeTest(int start, int end, int expectedCount) {
long count = java.util.stream.IntStream.range(start, end).count();
assertEquals(expectedCount, count);
}
static Stream<Arguments> rangeDataProvider() {
return Stream.of(
Arguments.of(1, 5, 4),
Arguments.of(0, 10, 10),
Arguments.of(-5, 5, 10)
);
}
}
}
enum TestEnum {
ACTIVE, INACTIVE, PENDING, COMPLETED
}
mvn test -Dtest=NestedWithParameterizedTestsTest OutputD:\example-projects\junit-5\nested-classes\junit-5-nested-with-parameterized-tests>mvn test -Dtest=NestedWithParameterizedTestsTest [INFO] Scanning for projects... [INFO] [INFO] --------< com.logicbig.example:nested-with-parameterized-tests >-------- [INFO] Building nested-with-parameterized-tests 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ nested-with-parameterized-tests --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\nested-classes\junit-5-nested-with-parameterized-tests\src\main\resources [INFO] [INFO] --- compiler:3.11.0:compile (default-compile) @ nested-with-parameterized-tests --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ nested-with-parameterized-tests --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] Copying 1 resource from src\test\resources to target\test-classes [INFO] [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ nested-with-parameterized-tests --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- surefire:3.5.0:test (default-test) @ nested-with-parameterized-tests --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] +--com.logicbig.example.NestedWithParameterizedTestsTest - 0.390 ss [INFO] | '-- [OK] systemInitializationTest - 0.044 ss [INFO] +--.--com.logicbig.example.NestedWithParameterizedTestsTest$DataCombinationTests - 0.148 ss [INFO] | | +-- [OK] rangeTest(int, int, int)[1] 1, 5, 4 - 0.033 ss [INFO] | | +-- [OK] rangeTest(int, int, int)[2] 0, 10, 10 - 0.003 ss [INFO] | | +-- [OK] rangeTest(int, int, int)[3] -5, 5, 10 - 0.003 ss [INFO] | | +-- [OK] evenOddTest(int, boolean)[1] 2, true - 0.003 ss [INFO] | | +-- [OK] evenOddTest(int, boolean)[2] 3, false - 0.001 ss [INFO] | | +-- [OK] evenOddTest(int, boolean)[3] 4, true - 0.001 ss [INFO] | | '-- [OK] evenOddTest(int, boolean)[4] 5, false - 0.001 ss [INFO] +--.--com.logicbig.example.NestedWithParameterizedTestsTest$StringOperations - 0.148 ss [INFO] | | +-- [OK] uppercaseTest(String, String)[1] hello, HELLO - 0.002 ss [INFO] | | +-- [OK] uppercaseTest(String, String)[2] jUnit, JUNIT - 0.001 ss [INFO] | | +-- [OK] uppercaseTest(String, String)[3] test, TEST - 0.003 ss [INFO] | | +-- [OK] stringLengthTest(String)[1] hello - 0.001 ss [INFO] | | +-- [OK] stringLengthTest(String)[2] world - 0.001 ss [INFO] | | +-- [OK] stringLengthTest(String)[3] junit - 0.002 ss [INFO] | | +-- [OK] stringLengthTest(String)[4] testing - 0.001 ss [INFO] | | +-- [OK] prefixedStringTest(String, String)[1] data, TEST_data - 0.002 ss [INFO] | | +-- [OK] prefixedStringTest(String, String)[2] value, TEST_value - 0.001 ss [INFO] | | '-- [OK] prefixedStringTest(String, String)[3] input, TEST_input - 0.001 ss [INFO] | '--.--com.logicbig.example.NestedWithParameterizedTestsTest$StringOperations$StringValidation - 0.081 ss [INFO] | | +-- [OK] enumTest(TestEnum)[1] ACTIVE - 0.002 ss [INFO] | | +-- [OK] enumTest(TestEnum)[2] PENDING - 0 ss [INFO] | | +-- [OK] blankStringTest(String)[1] null - 0.001 ss [INFO] | | +-- [OK] blankStringTest(String)[2] - 0.002 ss [INFO] | | +-- [OK] blankStringTest(String)[3] - 0.002 ss [INFO] | | +-- [OK] blankStringTest(String)[4] - 0.002 ss [INFO] | | '-- [OK] blankStringTest(String)[5] - 0.001 ss [INFO] +--.--com.logicbig.example.NestedWithParameterizedTestsTest$MathOperations - 0.148 ss [INFO] | | +-- [OK] additionTest(int, int, int)[1] 1, 2, 3 - 0.002 ss [INFO] | | +-- [OK] additionTest(int, int, int)[2] 5, 10, 15 - 0.002 ss [INFO] | | +-- [OK] additionTest(int, int, int)[3] -3, 3, 0 - 0.001 ss [INFO] | | +-- [OK] squareTest(int)[1] 1 - 0.001 ss [INFO] | | +-- [OK] squareTest(int)[2] 2 - 0.006 ss [INFO] | | +-- [OK] squareTest(int)[3] 3 - 0.001 ss [INFO] | | +-- [OK] squareTest(int)[4] 4 - 0.002 ss [INFO] | | '-- [OK] squareTest(int)[5] 5 - 0.002 ss [INFO] | '-----com.logicbig.example.NestedWithParameterizedTestsTest$MathOperations$AdvancedMath - 0.081 ss [INFO] | +-- [OK] csvFileTest(int, int)[1] 1, 2 - 0.001 ss [INFO] | +-- [OK] csvFileTest(int, int)[2] 2, 4 - 0.001 ss [INFO] | +-- [OK] csvFileTest(int, int)[3] 3, 6 - 0.001 ss [INFO] | +-- [OK] csvFileTest(int, int)[4] 5, 10 - 0.001 ss [INFO] | +-- [OK] csvFileTest(int, int)[5] 10, 20 - 0.001 ss [INFO] | +-- [OK] powerTest(int, int, double)[1] 2, 3, 8.0 - 0.001 ss [INFO] | +-- [OK] powerTest(int, int, double)[2] 5, 2, 25.0 - 0.001 ss [INFO] | '-- [OK] powerTest(int, int, double)[3] 10, 0, 1.0 - 0.001 ss [INFO] [INFO] Results: [INFO] [INFO] Tests run: 41, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.223 s [INFO] Finished at: 2025-12-06T10:17:07+08:00 [INFO] ------------------------------------------------------------------------
Example ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.7.0 - 6.0.1 Version compatibilities of junit-jupiter-engine with this example:
- 5.7.0
- 5.7.1
- 5.7.2
- 5.8.0
- 5.8.1
- 5.8.2
- 5.9.0
- 5.9.1
- 5.9.2
- 5.9.3
- 5.10.0
- 5.10.1
- 5.10.2
- 5.10.3
- 5.10.4
- 5.10.5
- 5.11.0
- 5.11.1
- 5.11.2
- 5.11.3
- 5.11.4
- 5.12.0
- 5.12.1
- 5.12.2
- 5.13.0
- 5.13.1
- 5.13.2
- 5.13.3
- 5.13.4
- 5.14.0
- 5.14.1
- 6.0.0
- 6.0.1
Versions in green have been tested.
- junit-jupiter-params 6.0.1 (Module "junit-jupiter-params" of JUnit)
- JDK 25
- Maven 3.9.11
|
|