Blog
navigate_next
Java
A Comprehensive Guide to Mastering Parameterized JUnit Tests
Shardul Lavekar
January 30, 2024

Ensuring the robustness and reliability of applications is crucial. As Java developers, we often rely on JUnit, a widely used testing framework, to write and run repeatable tests. In this post, we will delve into the nuances of parameterized tests in JUnit, focusing on their setup and practical implementation in the context of a simple e-commerce application.

1. Introduction to Parameterized Tests

Parameterized tests are a powerful feature in JUnit that allows developers to run the same test multiple times with different inputs. This not only improves the test coverage but also helps in identifying edge cases and potential bugs in the application.

2. Setting the Stage: The E-commerce Application

Imagine a simple e-commerce application where users can purchase products. A critical functionality of this application is the calculation of discounts based on various factors like user type, purchase amount, and special offers.

3. The Need for Parameterized Tests

To ensure our discount calculation logic is error-free, we must test it with various combinations of user types, purchase amounts, and offer conditions. This is where parameterized tests come into play, allowing us to test multiple scenarios efficiently.

4. Setting up for Parameterized JUnit Tests

To get started with parameterized tests in JUnit, you need to have JUnit 5 (Jupiter) in your project. If you're using Maven, include the following dependency in your <span class="pink">pom.xml</span>:


<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

Auto Generate JUnit Boilerplate with Unlogged IDE plugin

Let's also use the Unlogged IDE plugin to see how it makes unit testing easier. With the Unlogged plugin, you can:

  • Generate JUnit tests on the fly.
  • Inject mocks into code during runtime and replay the methods with mocks enabled.
  • Use direct invocation to test methods with specified arguments.

To set up the plugin in the IntelliJ IDEA, go to File → Settings or IntelliJ IDEA → Settings on macOS.

Select Plugins, and search for "Unlogged".

Click Install. Once installed, the plugin will show an "Installed" status.

We need to add some new dependencies and a plugin item to our <span class="pink">pom.xml</span> file.

Add these dependencies in the <span class="pink"><dependencies></span> tag:


<dependency>
  <artifactId>unlogged-sdk</artifactId>
  <groupId>video.bug</groupId>
  <version>0.1.44</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.16.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.16.0</version>
</dependency>

Run the Maven dependency resolve command to download the Unlogged dependencies and plugin:


mvn dependency:resolve

You should see the <span class="teal">BUILD SUCCESS</span> result:

Finally, reload the project using the Maven plugin. Click the Maven icon on the right side of the IntelliJ IDEA window, then select the refresh icon.

Here is how you can generate a JUnit Test Boilerplate.

5. Writing Our First Parameterized Test

Let's create a test class named <span class="teal">DiscountCalculatorTest</span>. The class will use the <span class="pink">@ParameterizedTest</span> annotation instead of the standard <span class="pink">@Test</span> annotation used in regular JUnit tests.


import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

public class DiscountCalculatorTest {

    @ParameterizedTest
    @MethodSource("provideDiscountExamples")
    void testDiscountCalculation(double purchaseAmount, double expectedDiscount) {
        DiscountCalculator calculator = new DiscountCalculator();
        double actualDiscount = calculator.calculateDiscount(purchaseAmount);
        assertEquals(expectedDiscount, actualDiscount);
    }
}

6. Providing Test Parameters with @MethodSource

In the above example, <span class="teal">provideDiscountExamples</span> is a method that will supply the test parameters. Let’s define it within the same class:


private static Stream provideDiscountExamples() {
    return Stream.of(
        Arguments.of(100.0, 10.0),
        Arguments.of(200.0, 20.0),
        Arguments.of(300.0, 30.0)
    );
}

Each <span class="teal">Arguments.of()</span> instance represents a set of parameters for the test method. In our case, it’s the purchase amount and the expected discount.

7. The DiscountCalculator Class

Now, let's briefly look at the <span class="pink">DiscountCalculator</span> class:


public class DiscountCalculator {
    public double calculateDiscount(double purchaseAmount) {
        // Discount calculation logic
        return purchaseAmount * 0.1; // Simplified for demonstration
    }
}

8. Expanding Test Coverage with @ArgumentsSource

JUnit Jupiter provides an <span class="pink">@ArgumentsSource</span> annotation for more complex parameter sources. It allows you to define a custom class that implements <span class="pink">ArgumentsProvider</span> to supply test arguments.

The arguments provider class, and your test class can be defined in separate files.


public class CustomArgumentsProvider implements ArgumentsProvider {
    @Override
    public Stream provideArguments(ExtensionContext context) {
        return Stream.of(
            Arguments.of(400.0, 40.0),
            Arguments.of(500.0, 50.0)
        );
    }
}

@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider.class)
void testWithCustomArgumentsSource(double purchaseAmount, double expectedDiscount) {
    DiscountCalculator calculator = new DiscountCalculator();
    double actualDiscount = calculator.calculateDiscount(purchaseAmount);
    assertEquals(expectedDiscount, actualDiscount);
}

9. Benefits of Parameterized Testing

Parameterized tests reduce the need for writing repetitive test cases and make it easier to add new scenarios just by adding new parameters. They are ideal for testing methods that behave differently based on input parameters.

Parameterized tests in JUnit are an indispensable tool for Java developers. They enable us to ensure the reliability and correctness of critical business logic with varied test inputs, ultimately leading to more resilient applications.

Shardul Lavekar
January 30, 2024
Use Unlogged to
mock instantly
record and replay methods
mock instantly
Install Plugin