Parameterized tests run the same test logic with different input values, reducing duplication and improving coverage.

Basic Parameterized Test

  import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;

class StringUtilsTest {

    @ParameterizedTest
    @ValueSource(strings = {"racecar", "level", "noon", "madam"})
    void shouldBePalindrome(String word) {
        assertTrue(StringUtils.isPalindrome(word));
    }

    @ParameterizedTest
    @ValueSource(ints = {1, 3, 5, 7, 9})
    void shouldBeOdd(int number) {
        assertTrue(number % 2 != 0);
    }
}
  

Argument Sources

@CsvSource

  @ParameterizedTest
@CsvSource({
    "2, 3, 5",
    "0, 0, 0",
    "-1, 1, 0",
    "100, 200, 300"
})
void shouldAdd(int a, int b, int expected) {
    assertEquals(expected, calculator.add(a, b));
}
  

With headers:

  @ParameterizedTest
@CsvSource(value = {
    "admin | true",
    "guest | false"
}, delimiter = '|')
void shouldCheckAdminRole(String role, boolean expected) {
    assertEquals(expected, userService.isAdmin(role));
}
  

@CsvFileSource

  @ParameterizedTest
@CsvFileSource(resources = "/test-data/users.csv", numLinesToSkip = 1)
void shouldValidateUser(String name, int age, String email) {
    assertTrue(userValidator.isValid(name, age, email));
}
  

@MethodSource

  @ParameterizedTest
@MethodSource("provideStringsForIsBlank")
void shouldCheckBlank(String input, boolean expected) {
    assertEquals(expected, StringUtils.isBlank(input));
}

static Stream<Arguments> provideStringsForIsBlank() {
    return Stream.of(
        Arguments.of(null, true),
        Arguments.of("", true),
        Arguments.of("  ", true),
        Arguments.of("hello", false)
    );
}
  

@EnumSource

  @ParameterizedTest
@EnumSource(value = Day.class, names = {"SATURDAY", "SUNDAY"})
void shouldBeWeekend(Day day) {
    assertTrue(day.isWeekend());
}
  

Custom Display Names

  @ParameterizedTest(name = "{index}: {0} + {1} = {2}")
@CsvSource({"1, 2, 3", "5, 5, 10"})
void shouldAddWithDisplay(int a, int b, int expected) {
    assertEquals(expected, calculator.add(a, b));
}
// Output: [1]: 1 + 2 = 3
  

Null and Empty Values

  @ParameterizedTest
@NullSource
@ValueSource(strings = {"", "  "})
void shouldRejectBlankStrings(String input) {
    assertFalse(validator.isValid(input));
}
  

Best Practices

  • Use @MethodSource for complex or computed test data
  • Use @CsvFileSource for large datasets maintained outside code
  • Provide meaningful @ParameterizedTest(name = "...") for readable reports
  • Keep parameterized tests focused — one behavior per test method
  • Combine with @Tag to categorize data-driven tests