Using JUnit 5 @ParameterizedTest with @ValueSource to Test Multiple Inputs
Learn how to replace duplicate test methods with a single JUnit 5 @ParameterizedTest driven by @ValueSource, including a runnable example, verification steps, and common pitfalls.
11 Mar 2026, 15:17 UTC

Quick answer
When you need the same assertion logic to run against several different values, annotate a test method with @ParameterizedTest and feed the values via @ValueSource. This eliminates duplicate test methods and makes the test suite easier to maintain.
How it works – a worked example
Suppose you want to verify that every non‑empty string has a length greater than zero. Instead of writing three separate @Test methods, you create a single parameterized test:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertTrue;
class StringLengthTest {
@ParameterizedTest
@ValueSource(strings = {"hello", "world", "junit"})
void lengthIsGreaterThanZero(String input) {
assertTrue(input.length() > 0,
() -> "String '" + input + "' should have length > 0");
}
}
The @ValueSource annotation supplies an array of literal values. JUnit creates a separate test invocation for each element, passing it as the input parameter. The test method runs three times, once with "hello", once with "world", and once with "junit".
Where to run the verification
From the root of a Maven‑managed project, execute:
# Run tests and generate a report
mvn clean test
If you use Gradle, the equivalent command is:
./gradlew clean test
Both commands require read access to the project directory and the ability to execute the build tool (no special privileges).
What to look for in the output
After the build finishes, open the generated test report:
- Maven:
target/surefire-reports/emailable-report.html - Gradle:
build/reports/tests/test/index.html
In the report you should see three entries for StringLengthTest.lengthIsGreaterThanZero, each displaying the parameter value (e.g., "[hello]", "[world]", "[junit]"). If you only see a single execution or the test is missing, the most common causes are:
- Using
@Testinstead of@ParameterizedTest– the method will be ignored by the Jupiter engine. - Missing the
junit-jupiter-enginedependency – the test framework cannot recognise the parameterized annotation. - Accidentally running the tests with a JUnit 4 runner (e.g.,
@RunWith(JUnit4.class)) – Jupiter extensions are not activated.
Limits of @ValueSource
@ValueSource can only supply:
- Primitive types (
int,long,double, …) StringliteralsClassobjects- Arrays of the above
For enums, use @EnumSource. For more complex objects or when you need to compute values dynamically, replace @ValueSource with @MethodSource or @CsvSource.
Common mistakes and how to avoid them
- Forgotten dependency – Add
org.junit.jupiter:junit-jupiter-engine:5.10.0(or newer) totestImplementationin Gradle ortestscope in Maven. - Null values – If the parameter type does not accept
null(e.g., a primitive), supplyingnullin@ValueSourcecauses aParameterResolutionException. Either change the type to its wrapper (Integer) or remove the null. - Expecting one run per class – Remember that each element in the array creates a separate test invocation; assertions are evaluated per invocation, not aggregated.
Practical way to confirm the test is working
Besides checking the HTML report, you can watch the console output during the build. A successful run will contain lines similar to:
StringLengthTest.lengthIsGreaterThanZero("hello")
StringLengthTest.lengthIsGreaterThanZero("world")
StringLengthTest.lengthIsGreaterThanZero("junit")
If you see only one line or the method name without parameters, revisit the annotation and dependency steps above.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.