Main Tutorials

JUnit 5 Parameterized Tests

junit 5 param test

This article shows you how to run a test multiple times with different arguments, so-called ‘Parameterized Tests’, let see the following ways to provide arguments to the test:

  • @ValueSource
  • @EnumSource
  • @MethodSource
  • @CsvSource
  • @CsvFileSource
  • @ArgumentsSource

We need junit-jupiter-params to support parameterized tests.

pom.xml

	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-engine</artifactId>
		<version>5.5.2</version>
		<scope>test</scope>
	</dependency>

	<!-- Parameterized Tests -->
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-params</artifactId>
		<version>5.5.2</version>
		<scope>test</scope>
	</dependency>

P.S Tested with JUnit 5.5.2

1. @ValueSource

1.1 For a single argument test.

ValueSourceTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class ValueSourceTest {

    // This test will run 3 times with different arguments
    @ParameterizedTest
    @ValueSource(ints = {1, 2, 3})
    void test_int_arrays(int arg) {
        assertTrue(arg > 0);
    }

    @ParameterizedTest(name = "#{index} - Run test with args={0}")
    @ValueSource(ints = {1, 2, 3})
    void test_int_arrays_custom_name(int arg) {
        assertTrue(arg > 0);
    }

    @ParameterizedTest(name = "#{index} - Run test with args={0}")
    @ValueSource(strings = {"apple", "banana", "orange"})
    void test_string_arrays_custom_name(String arg) {
        assertTrue(arg.length() > 1);
    }

}

Output

output

1.2 We can pass empty or null values into the test via @EmptySource, @NullSource or @NullAndEmptySource (since JUnit 5.4). Let see the following example to test an isEmpty() method.

ValueSourceEmptyTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EmptySource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class ValueSourceEmptyTest {

    boolean isEmpty(String input) {
        return (input == null || input.length() == 0);
    }

	// run 3 times, 1 for empty, 1 for null, 1 for ""
    @ParameterizedTest(name = "#{index} - isEmpty()? {0}")
    @EmptySource
    @NullSource
    //@NullAndEmptySource
    @ValueSource(strings = {""})
    void test_is_empty_true(String arg) {
        assertTrue(isEmpty(arg));
    }

    @ParameterizedTest(name = "#{index} - isEmpty()? {0}")
    @ValueSource(strings = {" ", "\n", "a", "\t"})
    void test_is_empty_false(String arg) {
        assertFalse(isEmpty(arg));
    }
    
}

Output

output

2. @EnumSource

2.1 Run tests that take Enum as an argument.

EnumSourceTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

import java.util.EnumSet;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE;

public class EnumSourceTest {

    enum Size {
        XXS, XS, S, M, L, XL, XXL, XXXL;
    }

    @ParameterizedTest
    @EnumSource(Size.class)
    void test_enum(Size size) {
        assertNotNull(size);
    }

    @ParameterizedTest(name = "#{index} - Is size contains {0}?")
    @EnumSource(value = Size.class, names = {"L", "XL", "XXL", "XXXL"})
    void test_enum_include(Size size) {
        assertTrue(EnumSet.allOf(Size.class).contains(size));
    }

    // Size = M, L, XL, XXL, XXXL
    @ParameterizedTest
    @EnumSource(value = Size.class, mode = EXCLUDE, names = {"XXS", "XS", "S"})
    void test_enum_exclude(Size size) {
        EnumSet<Size> excludeSmallSize = EnumSet.range(Size.M, Size.XXXL);
        assertTrue(excludeSmallSize.contains(size));
    }

}

Output.


$ java -jar junit-platform-console-standalone-1.5.2.jar 
	-cp "target/test-classes/" 
	--select-class com.mkyong.params.EnumSourceTest 
	--disable-ansi-colors
	
+-- JUnit Jupiter [OK]
| '-- EnumSourceTest [OK]
|   +-- test_enum_include(Size) [OK]
|   | +-- #1 - Is size contains L? [OK]
|   | +-- #2 - Is size contains XL? [OK]
|   | +-- #3 - Is size contains XXL? [OK]
|   | '-- #4 - Is size contains XXXL? [OK]
|   +-- test_enum(Size) [OK]
|   | +-- [1] XXS [OK]
|   | +-- [2] XS [OK]
|   | +-- [3] S [OK]
|   | +-- [4] M [OK]
|   | +-- [5] L [OK]
|   | +-- [6] XL [OK]
|   | +-- [7] XXL [OK]
|   | '-- [8] XXXL [OK]
|   '-- test_enum_exclude(Size) [OK]
|     +-- [1] M [OK]
|     +-- [2] L [OK]
|     +-- [3] XL [OK]
|     +-- [4] XXL [OK]
|     '-- [5] XXXL [OK]
'-- JUnit Vintage [OK]

3. @MethodSource

3.1 Run tests that take a static method to generate arguments.

MethodSourceTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class MethodSourceTest {

    @ParameterizedTest(name = "#{index} - Test with String : {0}")
    @MethodSource("stringProvider")
    void test_method_string(String arg) {
        assertNotNull(arg);
    }

    // this need static
    static Stream<String> stringProvider() {
        return Stream.of("java", "rust");
    }

    @ParameterizedTest(name = "#{index} - Test with Int : {0}")
    @MethodSource("rangeProvider")
    void test_method_int(int arg) {
        assertTrue(arg < 10);
    }

    // this need static
    static IntStream rangeProvider() {
        return IntStream.range(0, 10);
    }

}

Output


+-- JUnit Jupiter [OK]
| '-- MethodSourceTest [OK]
|   +-- test_method_int(int) [OK]
|   | +-- #1 - Test with Int : 0 [OK]
|   | +-- #2 - Test with Int : 1 [OK]
|   | +-- #3 - Test with Int : 2 [OK]
|   | +-- #4 - Test with Int : 3 [OK]
|   | +-- #5 - Test with Int : 4 [OK]
|   | +-- #6 - Test with Int : 5 [OK]
|   | +-- #7 - Test with Int : 6 [OK]
|   | +-- #8 - Test with Int : 7 [OK]
|   | +-- #9 - Test with Int : 8 [OK]
|   | '-- #10 - Test with Int : 9 [OK]
|   '-- test_method_string(String) [OK]
|     +-- #1 - Test with String : java [OK]
|     '-- #2 - Test with String : rust [OK]
'-- JUnit Vintage [OK]

3.2 Multiple arguments

MethodSourceMultiTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.arguments;

public class MethodSourceMultiTest {

    @ParameterizedTest
    @MethodSource("stringIntAndListProvider")
    void testWithMultiArgMethodSource(String str, int length, List<String> list) {
        assertTrue(str.length() > 0);
        assertEquals(length, list.size());
    }

    static Stream<Arguments> stringIntAndListProvider() {
        return Stream.of(
                arguments("abc", 3, Arrays.asList("a", "b", "c")),
                arguments("lemon", 2, Arrays.asList("x", "y"))
        );
    }

}

Output


+-- JUnit Jupiter [OK]
| '-- MethodSourceMultiTest [OK]
|   '-- test_method_multi(String, int, List) [OK]
|     +-- [1] abc, 3, [a, b, c] [OK]
|     '-- [2] lemon, 2, [x, y] [OK]
'-- JUnit Vintage [OK]

4. @CsvSource

4.1 Run tests that take a comma-separated values (csv) as arguments.

CsvSourceTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CsvSourceTest {

    @ParameterizedTest
    @CsvSource({
            "java,      4",
            "clojure,   7",
            "python,    6"
    })
    void test_csv(String str, int length) {
        assertEquals(length, str.length());
    }

}

5. @CsvFileSource

5.1 Imports comma-separated values (csv) from a file as arguments.

src/test/resources/simple.csv

java,      4
clojure,   7
python,    6
CsvFileSourceTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CsvFileSourceTest {

	// Skip the first line
    @ParameterizedTest
    @CsvFileSource(resources = "/simple.csv", numLinesToSkip = 1)
    void test_csv_file(String str, int length) {
        assertEquals(length, str.length());
    }

}

Output


+-- JUnit Jupiter [OK]
| '-- CsvFileSourceTest [OK]
|   '-- test_csv_file(String, int) [OK]
|     +-- [1] clojure, 7 [OK]
|     '-- [2] python, 6 [OK]
'-- JUnit Vintage [OK]

6. @ArgumentsSource

6.1 Create a reusable argument provider.

CustomArgumentsProvider.java

package com.mkyong.params;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;

import java.util.stream.Stream;

public class CustomArgumentsProvider implements ArgumentsProvider {

    @Override
    public Stream<? extends Arguments> 
		provideArguments(ExtensionContext extensionContext) throws Exception {
        return Stream.of("java", "rust", "kotlin").map(Arguments::of);
    }
}
ArgumentsSourceTest.java

package com.mkyong.params;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;

import static org.junit.jupiter.api.Assertions.assertNotNull;

public class ArgumentsSourceTest {

    @ParameterizedTest
    @ArgumentsSource(CustomArgumentsProvider.class)
    void test_argument_custom(String arg) {
        assertNotNull(arg);
    }

}

Download Source Code

$ git clone https://github.com/mkyong/junit-examples
$ cd junit5-examples
$ check src/test/java/com/mkyong/params/*.java

References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
1 Comment
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Thomas
2 years ago

Hi,

enforcing a failure in your test I don’t get the display name you have defined in your test.
I have beeing looking for @ParametritedTest where – from documentation – I should be
able to define something like:

   @DisplayName(“A very simple demo”)
   @ParameterizedTest(name = “#{index}: value={0}”)
   @ValueSource(strings={“abc”, “xyz”})
   public void testDemo(final String value) {
       System.out.println(value);
       assertTrue(value.equals(“abc”));
   }

Any ideas why the text “#1: value=abc” (and so on) is not shown (neither IDE nor on command line)?
(What I see:testDemo(String)[1] (and so on))

Kind Regards,
Thomas

APPENDIX

[INFO] ——————————————————-
[INFO] T E S T S
[INFO] ——————————————————-
[INFO] Running com.mkyong.core.MessageServiceTest
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.02 s <<< FAILURE! – in com.mkyong.core.MessageServiceTest
[ERROR] testGet Time elapsed: 0.016 s <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <Hello JUnit 55> but was: <Hello JUnit 5>
       at com.mkyong.core.MessageServiceTest.testGet(MessageServiceTest.java:13)

[INFO]
[INFO] Results:
[INFO]
[ERROR] Failures:
[ERROR]  MessageServiceTest.testGet:13 expected: <Hello JUnit 55> but was: <Hello JUnit 5>
[INFO]
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0