A parameterized test allows you to run a test against a varying set of data. If you find yourself calling the same test but with different inputs, over and over again, a parameterized test would help make your code cleaner. To create one in JUnit 5 you need to:
- Annotate the test method with
@ParameterizedTest
- Annotate the test method with at lease one source e.g.
@ValueSource
- Consume the arguments in the test method
The sections below describe some of the commonly used source annotations you can use to provide inputs to your test methods.
@ValueSource
This annotation lets you specify a single array of literal values that will be passed to your test method one by one, as shown in the example below:
@ParameterizedTest
@ValueSource(ints = {2, 4, 6})
void testIsEven(final int i) {
assertTrue(i % 2 == 0);
}
@CsvSource
This annotation allows you to specify an array of comma-separated values, which is useful if your test method takes multiple arguments. If you have a large number of arguments, you can use an ArgumentsAccessor
to extract the arguments as opposed to creating a method with a long parameter list. For example:
@ParameterizedTest(name = "Person with name {0} and age {1}")
@CsvSource({ "Alice, 28",
"Bob, 30" })
void testPerson(final String name, final int age) {
final Person p = new Person(name, age);
assertThat(p.getName(), is(name));
assertThat(p.getAge(), is(age));
}
@ParameterizedTest(name = "Person with name {0} and age {1}")
@CsvSource({ "Alice, 28",
"Bob, 30" })
void testPersonWithArgumentAccessor(final ArgumentsAccessor arguments) {
final String name = arguments.getString(0);
final int age = arguments.getInteger(1);
final Person p = new Person(name, age);
assertThat(p.getName(), is(name));
assertThat(p.getAge(), is(age));
}
By the way, note how I have also customised the display name of the test using the {0}
and {1}
argument placeholders.
@CsvFileSource
This annotation is similar to CsvSource
but allows you to load your test inputs from a CSV file on the classpath. For example:
@ParameterizedTest(name = "Person with name {0} and age {1}")
@CsvFileSource(resources = { "data.csv" })
void testPerson(final String name, final int age) {
final Person p = new Person(name, age);
assertThat(p.getName(), is(name));
assertThat(p.getAge(), is(age));
}
@MethodSource
This annotation allows you to specify a factory method which returns a stream of objects to be passed to your test method. If your test method has multiple arguments, your factory method should return a stream of Arguments
instances as shown in the example below:
import static org.junit.jupiter.params.provider.Arguments.*;
@ParameterizedTest(name = "{0} is sorted to {1}")
@MethodSource("dataProvider")
void testSort(final int[] input, final int[] expected) {
Arrays.sort(input);
assertArrayEquals(expected, input);
}
static Stream<Arguments> dataProvider() {
return Stream.of(
arguments(new int[] { 1, 2, 3 }, new int[] { 1, 2, 3 }),
arguments(new int[] { 3, 2, 1 }, new int[] { 1, 2, 3 }),
arguments(new int[] { 5, 5, 5 }, new int[] { 5, 5, 5 }));
}
For more information, see the JUnit 5 User Guide on Parameterized Tests.
If you're still on JUnit 4 (why?!), check out my previous post on Parameterized Tests in JUnit 4.