Guide: How to write Junit 5 Parameterized Tests

Introduction

In this blog we will learn how to use Junit 5 Parameterized tests.

Junit5 parameterized tests are used to run same test case for multiple inputs for verifying the robustness of the logic.

Let us say you want to check if the algorithm for prime number is working as expected for given list of input numbers.

We must have source to provide the data for testing the method.

There are multiple sources from which we can provide input data.

  1. ValueSource
  2. EnumSource
  3. CsvSource
  4. CsvFileSource
  5. MethodSource

To get started with parameterised test, we must provide maven dependency for the same.

I am using java 17 and below are the maven dependencies required.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.jupiter.version>5.8.1</junit.jupiter.version>
        <junit.platform.version>1.8.1</junit.platform.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
   
    </dependencies>

All the test cases should be annotated with @ParameterizedTest and not @Test

ValueSource

Using @ValueSource we can pass array of values of different data types for testing.

1
2
3
4
5
 @ParameterizedTest
 @ValueSource(ints ={2,4,6,8})
 public void testEvenNumbers(int number){
        Assertions.assertEquals(number%2 == 0,true);
    }

Above test cases runs 4 times, each time value from the array is mapped to method parameter which is int number.

Image alt

@ValueSource can have other data types like int,short,byte, double,float,char,String and Class.

Below is one more example where we are using strings as value source.

1
2
3
4
5
@ParameterizedTest
@ValueSource(strings ={"Tim","Jim","Lim"})
public void checkStringLength(String name){
        Assertions.assertEquals(name.length(),3);
}

Image alt

EnumSource

If you want to test your logic based on the values in enum you can use @EnumSource.

To explain this I have taken example of DayOfWeek inbuilt enum which has values of each day in a week.

1
2
3
4
5
6
    @ParameterizedTest
    @EnumSource(DayOfWeek.class)
    void checkDayOfTheWeek(DayOfWeek dayOfWeek) {
        int dayOfWeekNo = dayOfWeek.getValue();
        assertTrue(dayOfWeekNo >= 1 && dayOfWeekNo <= 7);
    }

In the above example each day has value from 1-Monday to 7-Sunday.

Above test case is going to run 7 times with each value of week and check if it is between 1 and 7(both inclusive).

dayofweek

CsvSource

If we want to pass multiple arguments to the method for testing which includes input value and expected result we can use @CsvSource for the same

@CsvSource takes array of values separated by comma just like a line in a CSV file.

Each entry is separated by comma and each value is mapped to method arguments.

1
2
3
4
5
@ParameterizedTest(name ="{index} {0} test case")
@CsvSource({"harish,HARISH","tim,TIM","Jane,JANE"})
void checkStringUppercase(String input, String expected) {
    Assertions.assertEquals(expected,input.toUpperCase());
}

In the above example we check if input value is converted to uppercase(expected).

Here first value harish is input value and HARISH is the expected value.

In the above example, comma is the default separator. You can use you own delimiter as shown below.

1
2
3
4
5
@ParameterizedTest
@CsvSource(value={"harish:HARISH","tim:TIM","Jane:JANE"}, delimiter = ':')
void checkStringUppercaseDelimiter(String input, String expected) {
    Assertions.assertEquals(expected,input.toUpperCase());
}

csvsource

CsvFileSource

We can pass CSV values required for testing from the file itself using @CsvFileSource.

This can come handy when input values are huge in numbers to be tested.

Ensure your csv file which contains testing data is present in classpath.

1
2
3
4
5
@ParameterizedTest
@CsvFileSource(resources = "/names.csv")
void checkStringUppercaseCsvFile(String input, String expected) {
Assertions.assertEquals(expected,input.toUpperCase());
}

In @CsvFileSource we can mention file name in resources attribute.

names.csv contains following content.

1
2
3
4
harish,HARISH
jim,JIM
tim,TIM
mary,MARY

csvfilesource

Method Source

Instead of providing input value as part of annotations we can use method to pass input value.

If the input values are in large number, specifying them in the annotations is not the good idea.

Using @MethodSource we just need to pass method name as part of arguments.

In the below example again we are testing whether string values are in uppercase as in @CsvSource.

But here we are using @MethodSource to take input value from the method itself.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@ParameterizedTest(name ="{index} {0} test case")
@MethodSource("provideStrings")
void checkArgumentsStringUppercase(String input, String expected) {
    Assertions.assertEquals(expected,input.toUpperCase());
}
private static Stream<Arguments> provideStrings(){
    return Stream.of(
            Arguments.of("jim","JIM"),
            Arguments.of("mary","MARY"),
            Arguments.of("jane","JANE")
    );
}

Method provideStrings returns stream of arguments.

First argument matched with the input value and second value of Arguments matches with expected value.

Conclusion

Thus we can have seen how to use parameterized test cases using multiple sources.

Hope this was helpful!