As a software engineer, I often encounter situations where I need to run the same test logic with different input values. Repeating the same test with minor variations not only clutters your test classes but also violates the DRY (Don't Repeat Yourself) principle.

That’s where parameterized tests come into play. Since JUnit 5, writing these tests has become much easier and cleaner. In this post, I’ll walk you through how to use parameterized tests effectively with JUnit 5.

What is a Parameterized Test?#

A parameterized test is a test that is executed multiple times with different parameters. Instead of writing multiple test methods for different inputs, you write one test method and provide it with different inputs.

This is perfect for testing algorithms, validations, or edge cases with minimal boilerplate.

How to Write a Parameterized Test?#

Imagine you’re testing a utility method, like one that checks if a string is a palindrome. You want to make sure it works for different inputs: racecar, madam, and radar.

If you’re testing 10 or 100 inputs, your test class grows unnecessarily and becomes harder to maintain.

This is where parameterized tests shine. Instead of writing a separate test for each input, you write one test method and let JUnit inject different values into it.

Here’s how you do that in JUnit 5:

@ParameterizedTest
@ValueSource(strings = {"racecar", "madam", "radar"})
void testIsPalindrome(String word) {
    assertTrue(isPalindrome(word));
}

Now, this single method will run three times, once for each value in the @ValueSource. If any of them fail, JUnit will show you which input caused the failure.

Advantages of this approach:

  • Eliminates repetitive code.
  • Easier to add/remove test cases.
  • More scalable and readable.
  • Ideal for input/output-based testing logic.

This makes your tests cleaner, more maintainable, and more expressive — especially as the number of test cases grows.

Available Parameter Sources in Junit 5#

JUnit 5 provides several annotations to supply parameters:

  • @ValueSource - for primitive types and strings.
  • @CsvSource – for multiple arguments using comma-separated values.
  • @CsvFileSource – loads values from a CSV file.
  • @EnumSource – useful when testing all enum values.
  • @MethodSource – calls a factory method from your class.
  • @ArgumentsSource – for more advanced use cases.

Multiple Parameters with @CsvSource#

Each line in @CsvSource represents one test case. The test will run four times, once for each input set.

@ParameterizedTest
@CsvSource({
        "true, true, true",
        "true, false, false",
        "false, true, false",
        "false, false, false"
})
void testShouldSendNotification(boolean isUserSubscribed, boolean isNotificationEnabled, boolean expected) {
    boolean result = shouldSendNotification(isUserSubscribed, isNotificationEnabled);
    assertEquals(expected, result);
}

private static boolean shouldSendNotification(boolean isUserSubscribed, boolean isNotificationEnabled) {
    return isUserSubscribed && isNotificationEnabled;
}

Using @MethodSource for Complex Objects#

Sometimes, you want to pass more complex objects:

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

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

This is more flexible and ideal for testing logic that requires multiple or non-primitive parameters.

Tips#

  • Use meaningful displayName values with @ParameterizedTest(name = "{index} => a={0}, b={1}") for better readability.
  • Combine with assertions like assertAll() for more expressive tests.
  • Keep your test data outside the test class with @CsvFileSource for better maintainability.

Conclusion#

Parameterized tests are a powerful way to write cleaner, more maintainable, and more scalable tests. They help you cover more scenarios with less code — and that’s always a win.

In my own work, parameterized tests have helped me avoid copy-pasting test logic and have made it easier to spot edge cases early.

If you haven’t used them before, I strongly recommend giving them a try in your next testing task.

Thanks for reading, I hope this post helps you use parameterized tests in unit tests in your applications. If you have any questions or suggestions, feel free to share them in the comments. 🚀