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.