Introduction

Parameterized tests are a JUnit feature that allows us to execute the same test multiple times with different input data by making use of the annotation @ParameterizedTest.

When to use Parameterized tests

Let’s say we want to test our brand-new class:

public class Calculator {

    public int divide(int num1, int num2) {
        return num1 / num2;
    }
}

(I know it’s pretty complex logic 😉). A typical scenario for this case is when we divide by zero, we will get an exception. Let’s test that with a simple test class:

class CalculatorTest {

    static Calculator calculator;

    @BeforeAll
    static void setup() {
        calculator = new Calculator();
    }

    @Test
    void it_should_throw_exception_when_divided_by_zero() {
        assertThrows(ArithmeticException.class, () -> calculator.divide(1, 0));
    }
}

So we execute the test and see what happens:

Test execution

Nice! the test has worked out as expected.

Now, let’s say we want to prove that this won’t happen for several inputs. We could start creating tests for each input, but if we want to test 6 different inputs, that would require 6 different tests.

For this kind of occasion, we can make use of the @ParameterizedTest annotation.

How to use Parameterized tests

The first thing we need to know in order to make use of Parameterized tests is to be able to pass our custom input data to the test we want to parameterize. There are multiple ways of doing this but let’s focus on the main ones.

So, the question is: How do we pass custom input data to parameterized tests?

@ValueSource

We can use the @ValueSource annotation for some simple input data

@ParameterizedTest
    @ValueSource(ints = {1, 2, -1, -2})
    void it_should_not_throw_exception_with_valid_values(int num) {
        assertDoesNotThrow(() -> calculator.divide(1, num));
    }

This creates 4 different tests, one for each value in the @ValueSource annotation.

Test execution with @ValueSource

@MethodSource

What if we want to pass multiple parameters, or use a more complex logic for the creation of the input data? We could use the @MethodSource annotation. This annotation will receive as a parameter, the name of the method that will return the input data for the test.

For instance, if we want to pass both numbers when dividing we could do something like this:

@ParameterizedTest
    @MethodSource("getValidValues")
    void it_should_not_throw_exception_with_valid_values(int num1, int num2) {
        assertDoesNotThrow(() -> calculator.divide(num1, num2));
    }

    private static Stream<Arguments> getValidValues() {
        return Stream.of(
                Arguments.of(1, 1),
                Arguments.of(10, 2),
                Arguments.of(-1, 1),
                Arguments.of(-1, -1),
                Arguments.of(0, 1)
        );
    }

We passed the String "getValidValues" to the @MethodSource that references to the new method that we have just created: private static Stream<Arguments> getValidValues()

This generates 5 different tests at the execution time, given that we are passing a Stream containing 5 different Arguments:

Test execution with @MethodSource

Some considerations:

  • Method must be static. Otherwise, we will receive the error: org.junit.platform.commons.PreconditionViolationException: Cannot invoke non-static method
  • When passing multiple arguments, we can use the class Arguments wrapped in Stream.
  • When passing a single argument, the method can just return a Stream of the required data type. For instance, if we want to pass Integers the method would just return Stream<Integer>.
private static Stream<Integer> getValidValues() {
        return Stream.of(1, 2, -1, -2);
}

Conclusion

We learned how to make use of the Parameterized tests feature in Java. It’s a really simple, yet so powerful feature that every Java developer should know about.

The provided example was not complex at all so let me know if it would be useful to write another post to get deeper into the topic with more realistic examples. For a 101 introduction, it should be enough though.

If you’re interested, check out more TeachingDev Java posts!