JUnit Parameterized Tests allow us to run a test method multiple times with different arguments. JUnit 5 provides a lot of ways to pass parameters to a test method.
JUnit Parameterized Tests
We need following additional dependency to use parameterized tests in our test cases.
1 2 3 4 5 6 7 8 |
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>5.2.0</version> <scope>test</scope> </dependency> |
We have to use @ParameterizedTest
with the test method instead of generic @Test
annotation.
We also have to provide a source that will generate the arguments for the method. There are many types of sources we can define and use in our parameterized test methods.
JUnit Parameterized Test with @ValueSource
This is the simplest form of parameterized test, we can use @ValueSource
to pass the arguments array. We can pass primitive data types array, string array or class array using ValueSource annotation.
1 2 3 4 5 6 7 8 9 10 11 12 |
@ParameterizedTest @ValueSource(ints = { 1, 2, 3 }) void test_ValueSource(int i) { System.out.println(i); } @ParameterizedTest @ValueSource(strings = { "1", "2", "3" }) void test_ValueSource_String(String s) { assertTrue(Integer.parseInt(s) < 5); } |
JUnit @ParameterizedTest with @EnumSource
@EnumSource
allows us to pass Enums to our test methods.
1 2 3 4 5 6 7 |
@ParameterizedTest @EnumSource(ElementType.class) void test_EnumSource(ElementType et) { System.out.println(et); } |
If we want only specific values from the Enum, we can do that using EnumSource name parameter.
1 2 3 4 5 6 7 |
@ParameterizedTest @EnumSource(value = ElementType.class, names = { "TYPE", "METHOD", "FIELD" }) void test_EnumSource_Extended(ElementType et) { assertTrue(EnumSet.of(ElementType.FIELD, ElementType.TYPE, ElementType.METHOD).contains(et)); } |
JUnit @ParameterizedTest with @MethodSource
We can use @MethodSource
to specify a factory method for test arguments. This method can be present in the same class or any other class too. The factory method should be static and return Strem, Iterator, Iterable or array of elements.
1 2 3 4 5 6 7 8 9 10 |
@ParameterizedTest @MethodSource("ms") void test_MethodSource(String s) { assertNotNull(s); } static Stream<String> ms() { return Stream.of("A", "B"); } |
We can also use MethodSource to pass multiple parameters to the test method. In this case, we will have to use Arguments
API. Let’s define a separate class with factory method source.
1 2 3 4 5 6 7 8 9 10 |
package com.journaldev.parameterizedtests; import java.util.stream.Stream; import org.junit.jupiter.params.provider.Arguments; public class MethodSources { public static Stream<Arguments> msMP() { return Stream.of(Arguments.of(1, "A"), Arguments.of(2, "B"), Arguments.of(3, "C")); } } |
Corresponding JUnit parameterized test method would be defined as:
1 2 3 4 5 6 7 8 |
@ParameterizedTest @MethodSource("com.journaldev.parameterizedtests.MethodSources#msMP") void test_MethodSource_MultipleParams(int i, String s) { assertTrue(4 > i); assertTrue(Arrays.asList("A", "B", "C").contains(s)); } |
JUnit MethodSource is very similar to TestNG DataProvider annotation.
JUnit @ParameterizedTest with @CsvSource
We can also pass CSV values to the test method. We can specify the delimiter for multiple arguments in the test method.
1 2 3 4 5 6 7 8 |
@ParameterizedTest @CsvSource(delimiter="|", value= {"1|'A'","2|B"}) void test_CsvSource(int i, String s) { assertTrue(3 > i); assertTrue(Arrays.asList("A", "B", "C").contains(s)); } |
JUnit Parameterized Test with CSV File
We can use @CsvFileSource
annotation to pass CSV data from a file to the parameterized test method. We can skip the header rows and define our custom delimiter too.
Let’s say we have country_code.csv
file defined as:
1 2 3 4 5 |
Country,TelephoneCode USA,1 India,91 |
Here is the test method where CSV file data will be used for arguments mapping.
1 2 3 4 5 6 7 8 |
@ParameterizedTest @CsvFileSource(resources = "/country_code.csv", numLinesToSkip = 1) void test_CsvFileSource(String country, int code) { assertNotNull(country); assertTrue(0 < code); } |
JUnit Parameterized Tests with Objects
So far we have used primitives and strings in our examples, but in real life, we have to pass objects most of the times. We can use @MethodSource
to achieve this functionality.
Let’s say we have a Book class defined as:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Book { private String title; // standard getter setters public Book(String t) { this.title = t; } @Override public String toString() { return title; } } |
Now we can pass Book object to our test methods using below factory method.
1 2 3 4 5 6 7 8 9 10 |
static Book[] mpBooks() { return new Book[] {new Book("Harry Potter"), new Book("Five Point Someone")}; } @ParameterizedTest @MethodSource("mpBooks") void test_MethodSource_Objects(Book b) { assertNotNull(b.getTitle()); } |
Notice that this time I am returning an array of Books, earlier I was returning Stream of elements.
JUnit Parameterized Tests Arguments Verification
If you are running test cases through Eclipse, you can check method arguments to make sure correct values are being passed to the parameterized tests.
JUnit Test Methods Argument Conversion
JUnit provides built-in support for many type converters. Some of them are int to long, string to boolean and vice versa, string to enum, date time objects. Below code will also work and JUnit will automatically call our Book class constructor to convert String values to Book object.
1 2 3 4 5 6 7 |
@ParameterizedTest @ValueSource(strings = {"Harry Potter", "Hamlet"}) void test_ValueSource_Objects(Book b) { assertNotNull(b.getTitle()); } |
However, this could lead to errors when test cases are executed if our Book class constructor changes. It’s better to use MethodSource and provide our own mechanism for object creation.
Summary
JUnit Parameterized Tests were a much-needed feature and it’s good to see so many options to provide arguments to our test methods.