Using Parameterized tests make it possible to run a test multiple times with different arguments. They are declared just like regular @Test methods but use the @ParameterizedTest annotation instead.
Additionally, we must declare at least one source that will provide the arguments for each invocation and then consume the arguments in the test method.
The following example demonstrates a parameterized test that uses the @ValueSource annotation to specify a String array as the source of arguments.
1 2 3 |
@ParameterizedTest @ValueSource(strings = {"madam", "radar", "dad"}) void palindromesTest(String inputText) { |
Sources of Arguments in Parameterized Tests
@ValueSource is one of the simplest possible sources. We can use this annotation to specify a single array of literal values and can only be used for providing a single argument per parameterized test invocation.
2.Null and Empty Sources
It provides a single null argument to the annotated @ParameterizedTest method. @NullSource cannot be used for a parameter that has a primitive type.
It provides a single empty argument to the annotated @ParameterizedTest method for parameters of the following types: java.lang.String, java.util.List, java.util.Set, java.util.Map, primitive arrays (e.g., int[], char[][], etc.), object arrays (e.g.,String[], Integer[][], etc.).
Subtypes of the supported types are not supported.
It is composed of annotation that combines the functionality of @NullSource and @EmptySource.
If you need to supply multiple blank strings to a parameterized test,you can achieve that using @ValueSource — for example, @ValueSource(strings = {” “, ” “, “\t”, “\n”}).
@EnumSource provides a convenient way to use Enum constants.
1 2 3 4 5 6 |
@ParameterizedTest @EnumSource(ChronoUnit.class) void testWithEnumSource(TemporalUnit unit) { assertNotNull(unit); } |
The annotation’s value attribute is optional. When it is omitted then the declared type of the first method parameter is used. The test will be failed if it does not reference an enum type. so, the value attribute is required in the above example because the method parameter is declared as TemporalUnit. To change the method parameter type to ChronoUnit allows you to omit the explicit enum type from the annotation as follows.
1 2 3 4 5 6 |
@ParameterizedTest @EnumSource void testWithEnumSourceWithAutoDetection(ChronoUnit unit) { assertNotNull(unit); } |
he annotation provides an optional names attribute that lets you specify which constants shall be used, like in the following example. If omitted, all constants will be used.
1 2 3 4 5 6 |
@ParameterizedTest @EnumSource(names = { "DAYS", "HOURS" }) void testWithEnumSourceInclude(ChronoUnit unit) { assertTrue(EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS).contains(unit)); } |
The @EnumSource annotation also provides an optional mode attribute that enables fine-grained control over which constants are passed to the test method. For example, We can exclude names from the enum constant pool or specify regular expressions as in the following examples.
1 2 3 4 5 |
@ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "ERAS", "FOREVER" }) void testWithEnumSourceExclude(ChronoUnit unit) { assertFalse(EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER).contains(unit)); } |
1 2 3 4 5 |
@ParameterizedTest@EnumSource(mode = Mode.MATCH_ALL, names = "^.*DAYS$") void testWithEnumSourceRegex(ChronoUnit unit) { assertTrue(unit.name().endsWith("DAYS")); } |
@MethodSource allows you to refer to one or more factory methods of the test
Factory methods within the test class must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS); whereas, factory methods in external classes must always be static. Additionally, such factory methods should not accept any arguments. class or external classes.
1 2 3 4 5 6 7 8 9 |
@ParameterizedTest @MethodSource("stringProvider") void testWithExplicitLocalMethodSource(String argument) { assertNotNull(argument); } static Stream<String> stringProvider() { return Stream.of("apple", "banana"); } |
@CsvSource allows you to express argument lists as comma-separated values (i.e., String literals).
1 2 3 4 5 6 7 8 9 10 |
@ParameterizedTest @CsvSource({ "apple, 1", "banana, 2", "'lemon, lime', 0xF1" }) void testWithCsvSource(String fruit, int rank) { assertNotNull(fruit); assertNotEquals(0, rank); } |
The default delimiter is a comma (,), but you can use another character by setting the delimiter attribute. Alternatively, the delimiter string attribute allows us to use a String delimiter instead of a single character. However, both delimiter attributes cannot be set simultaneously.
@CsvFileSource lets you use CSV files from the classpath. Here each line from a CSV file results in one invocation of the parameterized test.
1 2 3 4 5 6 |
@ParameterizedTest @CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1) void testWithCsvFileSource(String country, int reference) { assertNotNull(country); assertNotEquals(0, reference); } |
1 2 3 4 5 |
two-column.csv Country, reference Sweden, 1 Poland, 2 "United States of America", 3 |
@ArgumentsSource can be used to specify a custom, reusable ArgumentsProvider. You should note that an implementation of ArgumentsProvider interface must be declared as either a top-level class or as a static nested class.
1 2 3 4 5 6 7 8 9 10 11 12 |
@ParameterizedTest @ArgumentsSource(MyArgumentsProvider.class) void testWithArgumentsSource(String argument) { assertNotNull(argument); } public class MyArgumentsProvider implements ArgumentsProvider { @Override public Stream<? extends Arguments> provideArguments(ExtensionContext context) { return Stream.of("apple", "banana").map(Arguments::of); } } |
Let’s try to understand the above concept using a demo project
pom.xml
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 31 32 33 34 35 36 37 38 39 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kkjavatutorials</groupId> <artifactId>JUnit5ParameterizedTestsExample</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.target>8</maven.compiler.target> <maven.compiler.source>8</maven.compiler.source> <junit.jupiter.version>5.7.0</junit.jupiter.version> </properties> <dependencies> <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-engine</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> </project> |
myfile.csv
1 2 3 4 |
Country, reference India, 1 Germany, 2 United States of America, 3 |
MyUtils.java
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 31 32 33 |
package com.kkjavatutorials; public class MyUtils { /** * Method perform addition of two numbers * @param n1 first input number * @param n2 second input number * @return addition of two numbers */ public static int add(int n1, int n2) { return n1+n2; } /** * Method to check whether input String is Palindrome or not * @param inputText * @return return true if input is Palindrome else false */ public static boolean isPalindrome(String inputText) { return new StringBuffer(inputText).reverse().toString().equals(inputText); } /** * Method perform multiplication of two numbers * @param n1 first input number * @param n2 second input number * @return multiplication of two numbers */ public static Integer multiply(int n1, int n2) { return n1*n2; } } |
SourceProviders.java
1 2 3 4 5 6 7 8 9 10 |
package com.kkjavatutorials; import java.util.stream.Stream; public class SourceProviders { public static Stream<String> friutMethodSource() { return Stream.of("apple", "banana"); } } |
NAMES.java
1 2 3 4 5 |
package com.kkjavatutorials; public enum NAMES { KK,PK,MK } |
MyUtilsTest.java
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
package com.kkjavatutorials; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; import java.util.EnumSet; import java.util.stream.IntStream; import java.util.stream.Stream; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.provider.CsvFileSource; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource.Mode; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; /** * ParameterizedTest JUnit tests Example * @author KK JavaTutorials * */ public class MyUtilsTest { /** * Test with @ValueSource as Sources of Arguments * @param inputText */ @ParameterizedTest @ValueSource(strings = {"madam", "radar", "dad"}) void palindromesTest(String inputText) { //assertTrue(MyUtils.isPalindrome(inputText)); //assertTrue(()->MyUtils.isPalindrome(inputText)); //assertTrue(MyUtils.isPalindrome(inputText), "Not Palindrome"); //assertTrue(MyUtils.isPalindrome(inputText), ()->"Not Palindrome"); //assertTrue(()->MyUtils.isPalindrome(inputText), "Not Palindrome"); assertTrue(()->MyUtils.isPalindrome(inputText), ()->"Not Palindrome"); } /** * Test with @NullSource, @EmptySource and @ValueSource as Sources of Arguments * @param inputText */ @ParameterizedTest @NullSource @EmptySource @ValueSource(strings = { " ", " ", "\t", "\n" }) void nullEmptyAndBlankStringsTest(String inputText) { assertTrue(inputText == null || inputText.trim().isEmpty()); } /** * Test with @NullAndEmptySource and @ValueSource as Sources of Arguments * @param inputText */ @ParameterizedTest @NullAndEmptySource @ValueSource(strings = { " ", " ", "\t", "\n" }) void nullAndEmptySourceStringsTest(String inputText) { assertTrue(inputText == null || inputText.trim().isEmpty()); } /** * Test with @EnumSource as Sources of Arguments * @param temporalUnit */ @ParameterizedTest @EnumSource(value = ChronoUnit.class) void testWithEnumSource(TemporalUnit temporalUnit) { assertNotNull(temporalUnit); } /** * Test with @EnumSource as Sources of Arguments * @param temporalUnit */ @ParameterizedTest @EnumSource void testWithEnumSourceWithAutoDetection(ChronoUnit chronoUnit) { assertNotNull(chronoUnit); } /** * Test with @EnumSource as Sources of Arguments * @param chronoUnit */ @ParameterizedTest @EnumSource(names = { "WEEKS","DAYS",}) void testWithEnumSourceInclude(ChronoUnit chronoUnit) { assertTrue(EnumSet.of(ChronoUnit.WEEKS, ChronoUnit.DAYS).contains(chronoUnit)); } @ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "ERAS", "FOREVER","CENTURIES" }) void testWithEnumSourceExclude(ChronoUnit chronoUnit) { assertFalse(EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER,ChronoUnit.CENTURIES).contains(chronoUnit)); } @ParameterizedTest @EnumSource(mode = Mode.MATCH_ALL, names = "^.*DAYS$") void testWithEnumSourceRegex(ChronoUnit chronoUnit) { assertTrue(chronoUnit.name().endsWith("DAYS")); } /** * Test with Custom enum as Sources of Arguments * @param names */ @ParameterizedTest @EnumSource(NAMES.class) void testWithMyCustomEnumNames(NAMES names) { assertTrue(names.toString().length() == 2); } /** * Test with @MethodSource as Sources of Arguments * @param fruitName */ @ParameterizedTest @MethodSource("fruitNameProvider") void testWithExplicitLocalMethodSource(String fruitName) { assertNotNull(fruitName); } private static Stream<String> fruitNameProvider() { return Stream.of("apple", "banana","Orange"); } /** * ParameterizedTest with internal source method * @param fruitName */ @ParameterizedTest @MethodSource void testWithDefaultLocalMethodSource(String fruitName) { assertNotNull(fruitName); } private static Stream<String> testWithDefaultLocalMethodSource() { return Stream.of("apple", "banana"); } /** * ParameterizedTest with external source method * @param fruitName */ @ParameterizedTest @MethodSource("com.kkjavatutorials.SourceProviders#friutMethodSource") void testWithExternalMethodSource(String fruitName) { assertNotNull(fruitName); } /** * ParameterizedTest for range of numbers * @param argument */ @ParameterizedTest @MethodSource("numberRange") void testWithRangeMethodSource(int argument) { assertNotEquals(9, argument); } static IntStream numberRange() { return IntStream.range(0, 15).skip(10); } /** * Test with @CsvSource as Sources of Arguments * @param fruitName * @param fruitRank */ @ParameterizedTest @CsvSource({ "Ananas, 1", "Grapes, 2", "'lemon, lime', 0xF1" }) void testWithCsvSource(String fruitName, int fruitRank) { assertNotNull(fruitName); assertNotEquals(0, fruitRank); } /** * Test with @CsvFileSource as Sources of Argument * @param country * @param reference */ @ParameterizedTest @CsvFileSource(resources = "/myfile.csv", numLinesToSkip = 1) void testWithCsvFileSource(String country, int reference) { assertNotNull(country); assertNotEquals(0, reference); } @ParameterizedTest @ArgumentsSource(MyArgumentsProviderImpl.class) void testWithArgumentsSource(String argument) { assertNotNull(argument); } public static class MyArgumentsProviderImpl implements ArgumentsProvider { @Override public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) { return Stream.of("apple", "Grapes").map(Arguments::of); } } } |
The output of the above project:
You May Also Like:
Junit 5 Architecture
JUnit 5 Annotations
JUnit 5 Maven Dependency
JUnit 5 with Gradle Dependency
JUnit 5 Test Lifecycle
JUnit 5 @BeforeAll annotation example
Unit 5 @AfterAll annotation example
JUnit 5 @BeforeEach and @AfterEach annotation Example
JUnit 5 Display Names
Assertions in JUnit 5 Examples
Third-party Assertion Libraries support in JUnit 5
JUnit 5 Assumptions Examples
Conditional Test Execution in JUnit 5
JUnit 5 Nested Tests Example
JUnit 5 @Tag Annotation example
Test Execution Order in Junit 5
Dependency Injection and Testing in JUnit 5
Test Interfaces and Default Methods in JUnit 5
That’s all about Junit 5 Parameterized Tests with examples
If you have any feedback or suggestion please feel free to drop in below comment box.