Java > Testing in Java > Unit Testing > JUnit 5 Basics

Parameterized Tests in JUnit 5

This snippet demonstrates how to use parameterized tests in JUnit 5 to run the same test multiple times with different input values.

Project setup

To begin, ensure you have JUnit 5 added to your project. If you're using Maven, add the following dependency to your pom.xml:

<!-- JUnit 5 Dependency (Maven) -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

Parameterized Test Example

This code snippet shows a parameterized test using JUnit 5. Let's break it down:

  1. import org.junit.jupiter.params.ParameterizedTest;: This imports the ParameterizedTest annotation, which indicates that the method is a parameterized test.
  2. import org.junit.jupiter.params.provider.CsvSource;: This imports the CsvSource annotation, which provides the input values for the test.
  3. @ParameterizedTest: This annotation indicates that the testAddition method is a parameterized test.
  4. @CsvSource({"2, 3, 5", "-1, 5, 4", "0, 0, 0"}): This annotation provides three sets of input values for the test. Each string represents a set of values separated by commas. In this case, a, b, and the expected result.
  5. void testAddition(int a, int b, int expected): The test method takes three arguments: a, b, and expected. These arguments are automatically populated with the values from the CsvSource annotation.
  6. assertEquals(expected, result, "Addition should return the correct sum");: This assertion verifies that the expected value is equal to the actual result for each set of input values.

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class ParameterizedCalculatorTest {

    @ParameterizedTest
    @CsvSource({"2, 3, 5", "-1, 5, 4", "0, 0, 0"})
    void testAddition(int a, int b, int expected) {
        SimpleCalculator calculator = new SimpleCalculator();
        int result = calculator.add(a, b);
        assertEquals(expected, result, "Addition should return the correct sum");
    }
}

SimpleCalculator Class (Code Under Test)

This is the same simple calculator class from the previous example. The parameterized test verifies that its add method correctly adds two integers for multiple input values.

public class SimpleCalculator {

    public int add(int a, int b) {
        return a + b;
    }
}

Concepts Behind the Snippet

Parameterized tests are useful when you want to test the same code with multiple sets of input values. This can save time and reduce code duplication compared to writing separate test methods for each set of inputs. JUnit 5's @ParameterizedTest and @CsvSource annotations make it easy to define and run parameterized tests.

Real-Life Use Case

Consider validating user input. You might want to test a method that validates email addresses with various valid and invalid email address formats. A parameterized test would allow you to easily define a set of email addresses and their expected validation results, running the same validation logic for each case.

Best Practices

  • Choose Appropriate Data Sources: JUnit 5 offers various data source annotations like @CsvSource, @ValueSource, @MethodSource, and @EnumSource. Choose the one that best suits your needs.
  • Keep Parameterized Tests Focused: Each parameterized test should still focus on a single unit of code and a single aspect of its behavior.
  • Provide Clear Test Names: Use the name attribute of the @ParameterizedTest annotation to provide meaningful names for each test execution.

Interview Tip

Be prepared to explain the benefits of parameterized testing (reduced code duplication, increased test coverage) and to provide examples of when you would use them.

When to Use Them

Use parameterized tests when you need to test the same code with multiple sets of input values, especially when the logic is relatively simple and the variations are in the inputs.

Alternatives

Instead of parameterized tests, you could write separate test methods for each set of input values. However, this can lead to code duplication and make the tests harder to maintain.

Pros

  • Reduced Code Duplication: Parameterized tests allow you to reuse the same test logic for multiple sets of input values.
  • Increased Test Coverage: They make it easier to test a wider range of input values.
  • Improved Readability: They can make tests more concise and easier to understand.

Cons

  • Complexity: Parameterized tests can be more complex to set up than simple unit tests.
  • Debugging: Debugging parameterized tests can be more challenging, especially if the input values are complex.

FAQ

  • What is the purpose of the @CsvSource annotation?

    The @CsvSource annotation provides input values for the parameterized test in the form of comma-separated values. Each string in the @CsvSource annotation represents a set of input values for a single execution of the test.
  • What other data source annotations are available in JUnit 5 for parameterized tests?

    Besides @CsvSource, JUnit 5 offers @ValueSource (for single-value parameters), @MethodSource (for providing values from a method), and @EnumSource (for providing values from an enum).
  • How can I provide more complex input values for a parameterized test?

    For more complex input values, you can use the @MethodSource annotation and provide a method that returns a stream of arguments. This allows you to create custom objects or data structures for each test execution.