Skip to content

Parameterized Testing

If the inputs and outputs to be tested consist of a set of data, you can organize the test data and invoke the same test method with different datasets. This is known as parameterized testing.

Parameterized testing differs slightly from ordinary testing in that a test method needs to accept at least one parameter and then runs repeatedly with a set of parameters.

JUnit provides the @ParameterizedTest annotation for parameterized testing.

Example 1: Testing Math.abs()

Suppose we want to test Math.abs() with a set of positive numbers:

java
@ParameterizedTest
@ValueSource(ints = { 0, 1, 5, 100 })
void testAbs(int x) {
    assertEquals(x, Math.abs(x));
}

Then, test it with a set of negative numbers:

java
@ParameterizedTest
@ValueSource(ints = { -1, -5, -100 })
void testAbsNegative(int x) {
    assertEquals(-x, Math.abs(x));
}

Notice that the annotation for parameterized tests is @ParameterizedTest, not the regular @Test.

Example 2: Testing StringUtils.capitalize()

In real testing scenarios, things are often more complex. Suppose we have written a StringUtils.capitalize() method that capitalizes the first letter of a string and converts the subsequent letters to lowercase:

java
public class StringUtils {
    public static String capitalize(String s) {
        if (s.length() == 0) {
            return s;
        }
        return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
    }
}

To perform parameterized testing, we need to provide both the input and the expected output. Therefore, the test method must accept at least two parameters:

java
@ParameterizedTest
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

Now, the question arises: How do we pass the parameters?

The simplest method is to use the @MethodSource annotation, which allows us to write a static method with the same name to provide test parameters:

java
@ParameterizedTest
@MethodSource
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

static List<Arguments> testCapitalize() {
    return List.of( // arguments:
            Arguments.of("abc", "Abc"),
            Arguments.of("APPLE", "Apple"),
            Arguments.of("gooD", "Good"));
}

The above code is straightforward: the static method testCapitalize() returns a set of test parameters, each containing two String values, which are passed as parameters to the test method.

Tip

If the static method and the test method have different names, @MethodSource allows you to specify the method name. However, using the default same-named method is the most convenient.

Another way to pass test parameters is by using @CsvSource, where each string represents a line with multiple parameters separated by commas. Thus, the above test can be rewritten as follows:

java
@ParameterizedTest
@CsvSource({ "abc, Abc", "APPLE, Apple", "gooD, Good" })
void testCapitalize(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

If there are hundreds or thousands of test inputs, directly writing @CsvSource becomes inconvenient. In this case, you can place the test data in a separate CSV file and use @CsvFileSource:

java
@ParameterizedTest
@CsvFileSource(resources = { "/test-capitalize.csv" })
void testCapitalizeUsingCsvFile(String input, String result) {
    assertEquals(result, StringUtils.capitalize(input));
}

JUnit only looks for the specified CSV file in the classpath, so the test-capitalize.csv file should be placed in the test directory with the following content:

apple, Apple
HELLO, Hello
JUnit, Junit
reSource, Resource

Summary

  • Parameterized Testing: Allows providing a set of test data to repeatedly test a single test method with different inputs and expected outputs.

  • Data Sources:

    • Inline Data: Use annotations like @ValueSource, @CsvSource, and @MethodSource to supply test data directly within the test code.
    • External Files: Use @CsvFileSource to supply test data from external CSV files, which is useful for large datasets.
  • Flexibility: Parameters can be hardcoded within the test code or sourced from external files, enabling comprehensive and scalable testing strategies.

By leveraging parameterized testing, you can efficiently test methods with multiple input scenarios, ensuring robust and reliable code.

Parameterized Testing has loaded