Parameterized Tests in JUnit 5
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:
1 2 3 4 5 | @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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | @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:
1 2 3 4 5 6 7 | @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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | 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.
Published on Java Code Geeks with permission by Fahd Shariff, partner at our JCG program. See the original article here: Parameterized Tests in JUnit 5 Opinions expressed by Java Code Geeks contributors are their own. |