Introduction

In this example we will create Junit 5 parameterized tests. In our previous example we have seen how to configure Junit 5 for Java projects. In parameterized tests it is possible to run a test multiple times with different arguments. The @ParameterizedTest annotation is used in the regular @Test methods to indicate parameterized tests. In addition, at least one source must be declared to provide the arguments for each invocation in the test method.

We will see different sources of data for invoking the tests multiple times with different parameters. The sources can be @ValueSource, @MethodSource, @EnumSource, @ArgumentSource, @EmptySource, @NullSource, @EmptySource, @CsvSource.

Prerequisites

Eclipse 4.12, At least Java 8, Junit 5

Value Source

The following example demonstrates a parameterized test that uses the @ValueSource annotation to specify a String array as the source of arguments.

@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromesValueSource(String candidate) {
	assertTrue(PalindromChecker.isPalindrome(candidate));
}

When executing the above parameterized test method, each invocation will be reported separately.

The isPalindrome() method is given below:

public static boolean isPalindrome(final String str) {
	StringBuilder sb = new StringBuilder(str);

	return str.equals(sb.reverse().toString());
}

The following types of literal values are supported by @ValueSource.

short, byte, int, long, float, double, char, boolean, java.lang.String, java.lang.Class

Method Source

@MethodSource allows you to refer to one or more factory methods of the test class or external classes.

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. In addition, such factory methods must not accept any arguments.

@ParameterizedTest
@MethodSource("values")
void palindromesMethodSource(String candidate) {
	assertTrue(PalindromChecker.isPalindrome(candidate));
}

Notice the @MethodSource annotation expects the method values() that provides input data to the test method palindromesMethodSource() and the parameterized test method accepts single argument, so single value will be passed at a time to the test method.

Here is the method values():

public static String[] values() {
	return new String[] { "racecar", "radar", "able was I ere I saw elba", "madam" };
}

You can also define the values() method as follows using Stream of instances of parameter types.

public static Stream<String> values() {
	return Stream.of("racecar", "radar", "able was I ere I saw elba", "madam");
}

If you do not explicitly provide a factory method name via @MethodSource, JUnit Jupiter will search for a factory method that has the same name as the current @ParameterizedTest method by convention.

For test method having two arguments can be written as follows:

public static Object[] strings() {
	return new Object[][] { //
			{ "str", true }, //
			{ "str", true }, //
			{ "str", false }, //
			{ "exactly 5 objects", false }, //
			{ "at least 5 objects", false }, //
			{ "\"more than\" 5 objects", false },//
	};
}

@ParameterizedTest
@MethodSource("strings")
void stringsMethodSource(String str, boolean trueFalse) {
	assertEquals(trueFalse, StringChecker.check(str, trueFalse));
}

The check() method is defined as below:

public static boolean check(String str, boolean trueFalse) {
	return str.equals("str") && trueFalse;
}

An external, static factory method can be referenced by providing its fully qualified method name as demonstrated in the following example.

Let’s say we have defined the following method in class PalindromChecker class:

public static Stream<String> values() {
	return Stream.of("racecar", "radar", "able was I ere I saw elba", "madam");
}

Then we can access the above method in the following way:

@ParameterizedTest
@MethodSource("com.roytuts.junit.parameterized.PalindromChecker#values")
void palindromesMethodSourceExternal(String candidate) {
	assertTrue(PalindromChecker.isPalindrome(candidate));
}

Null and Empty Sources

To verify the proper behavior of our software we need to check for bad inputs, such as, null and empty values.

@NullSource provides a single null argument to the annotated @ParameterizedTest method and cannot be used for a parameter that has a primitive type.

@EmptySource 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.

@NullAndEmptySource is a composed annotation that combines the functionality of @NullSource and @EmptySource.

The following examples shows how to apply the above annotations:

@ParameterizedTest
@NullSource
@ValueSource(strings = { " ", "", "\t", "\n" })
void stringNullSource(String str) {
	assertTrue(str == null || str.trim().isEmpty());
}

@ParameterizedTest
@EmptySource
@ValueSource(strings = { " ", "", "\t", "\n" })
void stringEmptySource(String str) {
	assertTrue(str == null || str.trim().isEmpty());
}

@ParameterizedTest
// @NullSource
// @EmptySource
@NullAndEmptySource
@ValueSource(strings = { " ", "", "\t", "\n" })
void stringNullAndEmptySource(String str) {
	assertTrue(str == null || str.trim().isEmpty());
}

@NullAndEmptySource can be replaced by putting together @NullSource and @EmptySource annotations.

Enum Source

@EnumSource provides a convenient way to use Enum constants.

The annotation’s value attribute is optional. When omitted, the declared type of the first method parameter is used.

The annotation provides an optional names attribute that lets you specify which constants shall be used as shown in the following example. If omitted, all constants will be used.

The @EnumSource annotation also provides an optional mode attribute that enables fine-grained control over which constants are passed to the test method.

@ParameterizedTest
@EnumSource(names = { "DAYS", "HOURS" }, value = ChronoUnit.class)
void enumSource(ChronoUnit unit) {
	assertTrue(EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS).contains(unit));
}

@ParameterizedTest
@EnumSource(mode = Mode.EXCLUDE, names = { "ERAS", "FOREVER" }, value = ChronoUnit.class)
void enumSourceExclude(ChronoUnit unit) {
	assertFalse(EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER).contains(unit));
}

@ParameterizedTest
@EnumSource(mode = Mode.MATCH_ALL, names = "^.*DAYS$", value = ChronoUnit.class)
void enumSourceRegex(ChronoUnit unit) {
	assertTrue(unit.name().endsWith("DAYS"));
}

Csv Source

@CsvSource allows you to express argument lists as comma-separated values (i.e., String literals).

The default delimiter is a comma (,), but you can use another character by setting the delimiter attribute. Alternatively, the delimiterString attribute allows you to use a String delimiter instead of a single character. However, both delimiter attributes cannot be set simultaneously.

@ParameterizedTest
@CsvSource({ "Jane, Doe, F, 1990-05-20", "John, Doe, M, 1990-10-22" })
void testWithArgumentsAccessor(ArgumentsAccessor arguments) {
	Person person = new Person(arguments.getString(0), arguments.getString(1), arguments.get(2, Gender.class),
			arguments.get(3, LocalDate.class));

	if (person.getFirstName().equals("Jane")) {
		assertEquals(Gender.F, person.getGender());
	} else {
		assertEquals(Gender.M, person.getGender());
	}
	assertEquals("Doe", person.getLastName());
	assertEquals(1990, person.getDateOfBirth().getYear());
}

Here is the class Person:

package com.roytuts.junit.parameterized;

import java.time.LocalDate;

public class Person {

	private String firstName;
	private String lastName;
	private Gender gender;
	private LocalDate dateOfBirth;

	public Person(String firstName, String lastName, Gender gender, LocalDate dateOfBirth) {
		this.firstName = firstName;
		this.lastName = lastName;
		this.gender = gender;
		this.dateOfBirth = dateOfBirth;
	}

        //getters and setters
}

Gender enum is given below:

package com.roytuts.junit.parameterized;

public enum Gender {

	M, F

}

Argument Source

@ArgumentsSource can be used to specify a custom, reusable ArgumentsProvider. Note that an implementation of ArgumentsProvider must be declared as either a top-level class or as a static nested class.

@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider.class)
void argumentsSource(String argument) {
	assertNotNull(argument);
}

Testing the Tests

Running the above class will run all the test cases.

Source Code

Download

Thanks for reading.

Tags: