Close

JUnit 5 - Nested with Parameterized Tests

[Last Updated: Dec 6, 2025]

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.csv

input,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

Output

D:\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 Project

Dependencies and Technologies Used:

  • junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
     Version Compatibility: 5.7.0 - 6.0.1Version List
    ×

    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

Nested with Parameterized Tests Select All Download
  • junit-5-nested-with-parameterized-tests
    • src
      • test
        • java
          • com
            • logicbig
              • example
                • NestedWithParameterizedTestsTest.java
          • resources

    See Also