Open In App

Inject Parameters into JUnit Jupiter Unit Tests

Last Updated : 23 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

JUnit 5 (also known as JUnit Jupiter) introduced several new features to enhance the process of writing unit tests. One of the key enhancements is the ability to inject parameters into test methods, simplifying the test setup and making the code more maintainable. Parameterized tests allow the test method to run multiple times with different arguments. In this article, we will explore how to inject parameters into JUnit Jupiter unit tests using various sources like value providers and arguments.

Injecting Parameters in JUnit 5

Parameterized tests in JUnit 5 (JUnit Jupiter) enable running the same test logic with different data sets, reducing redundancy and improving test coverage. By injecting parameters into test methods, we avoid the need for repetitive test code for different cases. The framework provides several annotations to facilitate parameter injection, making it flexible and powerful.

Why Use Parameterized Tests?

In traditional unit testing, we often need to test methods with multiple inputs, leading to repetitive test cases. Parameterized tests solve this problem by allowing a single test method to run multiple times with different arguments. The key benefits of using parameterized tests include:

  • Code Reusability: A single test method can handle multiple inputs, avoiding the need to write separate methods for each test case.
  • Test Coverage: Running the same test logic with various inputs ensures better test coverage.
  • Readability: The code becomes cleaner and easier to understand, as we eliminate duplicate test logic.

How Parameterized Tests Work

JUnit 5 introduces the @ParameterizedTest annotation to mark the test method as the parameterized. This annotation can be used in the conjunction with various argument source annotations, which specify the source of the input data. The test method can be executed once for each set of the input data, with the parameters being automatically injected by the JUnit framework.

Here are the key elements used in parameterized tests:

  1. Test Method: The method where the actual test logic is written. This method will be executed multiple times with different parameters.
  2. Source Annotations: These annotations provides the input parameters. Some of the common ones include:
    • @ValueSource: This annotation provides literal values such as the int, String, double, etc.
    • @CsvSource: This annotation supplies multiple sets of the arguments in the comma-separated format.
    • @EnumSource: This annotation passes values from the enum.
    • @MethodSource: This annotation supplies parameters from a static method, allowing for more complex input preparation.
    • @ArgumentsSource: This annotation uses the custom provider to generate parameters dynamically.

Key Annotations in JUnit 5 Parameter Injection

1. @ParameterizedTest

@ParameterizedTest annotation designates the method as a parameterized test. Unlike standard tests, this method will receive parameters injected by JUnit 5 based on the provided argument sources.

2. @ValueSource

The simplest way to inject values. It supports basic data types like int, String, double, etc. You can provide a list of literal values that will be passed to the test method in each invocation.

Example:

@ParameterizedTest
@ValueSource(strings = {"apple", "banana", "orange"})
void testWithStringValue(String fruit) {
assertNotNull(fruit); // Asserts that the fruit is not null
}

3. @CsvSource

Allows injecting multiple arguments into the test method by providing a comma-separated list of values, useful for testing methods that take multiple parameters.

Example:

@ParameterizedTest
@CsvSource({
"apple, 5",
"banana, 6"
})
void testWithCsvSource(String fruit, int length) {
assertEquals(length, fruit.length()); // Asserts that the length matches the expected value
}

4. @MethodSource

Used when input parameters are complex or need to be generated programmatically. It refers to a static method that provides a stream of arguments.

Example:

@ParameterizedTest
@MethodSource("stringProvider")
void testWithMethodSource(String argument) {
assertTrue(argument.startsWith("a")); // Asserts that the argument starts with 'a'
}

static Stream<String> stringProvider() {
return Stream.of("apple", "avocado"); // Provides test arguments
}

5. @EnumSource

Ideal for testing with all or a subset of Enum constants. It injects the Enum values into the test method.

Example:

enum Season { WINTER, SPRING, SUMMER, FALL }

@ParameterizedTest
@EnumSource(Season.class)
void testWithEnumSource(Season season) {
assertNotNull(season); // Asserts that the season is not null
}

6. @ArgumentsSource

For advanced scenarios, it defines a custom arguments provider by implementing the ArgumentsProvider interface, allowing full control over how arguments are generated.

Example:

static class CustomArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(Arguments.of("apple", 5), Arguments.of("banana", 6)); // Provides custom test arguments
}
}

@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider.class)
void testWithCustomArguments(String fruit, int length) {
assertEquals(length, fruit.length()); // Asserts that the length matches the expected value
}

Implementation of Parameterized Tests in JUnit 5

This example project demonstrates how to inject the parameters into JUnit 5 unit tests.

Step 1: Create a New Maven Project

Create a new Maven project using IntelliJ IDEA with the following options:

  • Name: junit5-parameterized-tests
  • Build System: Maven

Click on the Create button.

Project Metadata

Project Structure

After project creation done successfully, the folder structure will look like the below image:

Project Folder Structure

Step 2: Add Dependencies to pom.xml

Open the pom.xml and add the JUnit 5 dependencies to the Maven project.

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0"
         xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gfg</groupId>
    <artifactId>junit5-parameterized-tests</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.11.1</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.11.1</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.11.1</version>
            <scope>test</scope>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <!-- Maven Surefire plugin to run JUnit tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

</project>

Step 3: Create a Calculator Class

Create a simple Calculator class that performs basic operations like addition, subtraction, multiplication, and division. This class serves as the subject of our unit tests.

Calculator.java:

Java
package com.gfg;

public class Calculator {

    // Adds two integers and returns the result
    public int add(int a, int b) 
    {
        return a + b;
    }

    // Subtracts the second integer from the first and returns the result
    public int subtract(int a, int b) 
    {
        return a - b;
    }

    // Multiplies two integers and returns the result
    public int multiply(int a, int b) 
    {
        return a * b;
    }

    // Divides the first integer by the second and returns the result
    // Throws an exception if the second integer is zero
    public int divide(int a, int b) 
    {
        if (b == 0) {
            throw new IllegalArgumentException("Division by zero is not allowed");
        }
        return a / b;
    }
}

Step 4: Main Class

Add a simple main class to use the Calculator service directly. This class simulates a basic command-line application that performs arithmetic operations.

Main. java:

Java
package com.gfg;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator(); // Creates an instance of the Calculator class
        Scanner scanner = new Scanner(System.in); // Scanner for user input

        System.out.println("Welcome to the Calculator App");
        System.out.println("Please choose an operation: add, subtract, multiply, divide");
        String operation = scanner.nextLine(); // Reads the operation choice

        System.out.println("Enter the first number: ");
        int num1 = scanner.nextInt(); // Reads the first number

        System.out.println("Enter the second number: ");
        int num2 = scanner.nextInt(); // Reads the second number

        int result = 0; // Variable to store the result

        // Performs the chosen operation
        switch (operation.toLowerCase()) {
            case "add":
                result = calculator.add(num1, num2);
                break;
            case "subtract":
                result = calculator.subtract(num1, num2);
                break;
            case "multiply":
                result = calculator.multiply(num1, num2);
                break;
            case "divide":
                result = calculator.divide(num1, num2);
                break;
            default:
                System.out.println("Invalid operation!"); // Error message for invalid operation
                return;
        }
        System.out.println("The result is: " + result); // Outputs the result
    }
}

Step 5: Create Parameterized Tests for Calculator

Create the CalculatorTest class, which will contain the tests for our Calculator class. This file will demonstrate the use of the @ParameterizedTest and various parameter sources such as the @ValueSource, @CsvSource, and @MethodSource.

CalculatorTest.java:

Java
package com.gfg;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

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

class CalculatorTest {

    private final Calculator calculator = new Calculator(); // Instance of the Calculator for testing

    // Tests the add method with various inputs
    @ParameterizedTest
    @CsvSource({
        "1, 2, 3", // 1 + 2 = 3
        "5, 6, 11", // 5 + 6 = 11
        "-1, 1, 0" // -1 + 1 = 0
    })
    void testAdd(int a, int b, int expected) 
    {
        assertEquals(expected, calculator.add(a, b)); // Asserts that the result matches the expected value
    }

    // Tests the subtract method with various inputs
    @ParameterizedTest
    @CsvSource({
        "5, 3, 2", // 5 - 3 = 2
        "10, 4, 6", // 10 - 4 = 6
        "-1, -1, 0" // -1 - -1 = 0
    })
    void testSubtract(int a, int b, int expected) 
    {
        assertEquals(expected, calculator.subtract(a, b)); // Asserts that the result matches the expected value
    }

    // Tests the multiply method with various inputs
    @ParameterizedTest
    @ValueSource(ints = {2, 3, 5})
    void testMultiply(int number) {
        assertEquals(0, calculator.multiply(number, 0)); // Asserts that multiplying by 0 results in 0
    }

    // Tests the divide method with valid inputs
    @ParameterizedTest
    @CsvSource({
        "6, 2, 3", // 6 / 2 = 3
        "9, 3, 3", // 9 / 3 = 3
        "0, 1, 0" // 0 / 1 = 0
    })
    void testDivide(int a, int b, int expected) {
        assertEquals(expected, calculator.divide(a, b)); // Asserts that the result matches the expected value
    }

    // Tests the divide method for division by zero
    @Test
    void testDivideByZero() {
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
            calculator.divide(10, 0); // This should throw an exception
        });
        assertEquals("Division by zero is not allowed", exception.getMessage()); // Asserts that the exception message is correct
    }
}

Step 6: Run the Application

Once the project is completed, it will run and show the below output:

Output

Step 7: Running the Tests

We can use the following command to run the tests:

mvn test

When we run the tests, we should see the following output:

Test Output

This example project demonstrates how to implement the parameterized tests in the JUnit 5 using annotations like @ValueSource, @CsvSource, and @MethodSource. The ability to inject parameters makes the unit tests more flexible, reusable, and easier to maintain.


Article Tags :

Similar Reads