Writing Parameterized Tests With JUnit 4 (2025)

After we have finished this lesson, we

  • Know how we can write parameterized tests by using the test runner provided by JUnit 4.
  • Understand why we shouldn’t write parameterized tests that use the test runner provided by JUnit 4.
  • Can write parameterized tests by using the JUnitParams library.

This lesson assumes that:

  • You are familiar with JUnit 4
  • You can use custom test runners
  • You know how you can run unit tests by using Maven or Gradle

What Is a Parameterized Test?

A parameterized test is a test case that is invoked by using a predefined input and expected output. These tests are typically used to test classes that have no external dependencies and that provide methods which have no side effects.

Let’s move on and take a look at the system under test.

Introduction to System Under Test

During this lesson we will write unit tests for a simple Calculator class. It has one method called sum() and this method returns the sum of two integers. The source code of the Calculator class looks as follows:

public class Calculator { public int sum(int first, int second) { return first + second; }}

Next, we will write parameterized tests for the sum() method by using JUnit 4.

Writing Parameterized Tests With JUnit 4

We can write parameterized tests with JUnit 4 by following these steps:

First, we have to ensure that our unit tests are run by the Parameterized class. After we have configured the used test runner, the source code of our test class looks as follows:

import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;@RunWith(Parameterized.class)@Category(UnitTest.class)public class StandardCalculatorTest {}

Second, we have to create a static method that returns a Collection of Object arrays, and annotate this method with the @Parameters annotation. This method creates the input provided to the tested object and describes the expected output.

After we have created this method, the source code of our test class looks as follows:

import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;import org.junit.runners.Parameterized.Parameters;import java.util.Arrays;import java.util.Collection;@RunWith(Parameterized.class)@Category(UnitTest.class)public class StandardCalculatorTest { @Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 } }); }}

Third, we have to add the required fields into our test class. These fields contain the tested object, the input provided to the tested object, and the expected output.

After we have added these fields into our test class, its source code looks as follows:

import org.junit.Test;import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;import org.junit.runners.Parameterized.Parameters;import java.util.Arrays;import java.util.Collection;import static org.assertj.core.api.Assertions.assertThat;@RunWith(Parameterized.class)@Category(UnitTest.class)public class StandardCalculatorTest { @Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 } }); } private final Calculator calculator; private final int first; private final int second; private final int expectedSum;}

Fourth, we have to write a constructor that takes the input and output data as constructor arguments. These arguments are provided by the custom test runner which obtains them by invoking the data() method. This constructor also creates the tested object.

After we have created this constructor, the source code of our test class looks as follows:

import org.junit.Test;import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;import org.junit.runners.Parameterized.Parameters;import java.util.Arrays;import java.util.Collection;import static org.assertj.core.api.Assertions.assertThat;@RunWith(Parameterized.class)@Category(UnitTest.class)public class StandardCalculatorTest { @Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 } }); } private final Calculator calculator; private final int first; private final int second; private final int expectedSum; public StandardCalculatorTest(int first, int second, int expectedSum) { this.calculator = new Calculator(); this.first = first; this.second = second; this.expectedSum = expectedSum; }}

Fifth, we have to write one unit test which verifies that the sum() method of the Calculator class is working as expected when we use the test data provided by the data() method.

After we have written this unit test, the source code of our test class looks as follows:

import org.junit.Test;import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;import org.junit.runners.Parameterized.Parameters;import java.util.Arrays;import java.util.Collection;import static org.assertj.core.api.Assertions.assertThat;@RunWith(Parameterized.class)@Category(UnitTest.class)public class StandardCalculatorTest { @Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 } }); } private final Calculator calculator; private final int first; private final int second; private final int expectedSum; public StandardCalculatorTest(int first, int second, int expectedSum) { this.calculator = new Calculator(); this.first = first; this.second = second; this.expectedSum = expectedSum; } @Test public void shouldReturnCorrectSum() { int actualSum = calculator.sum(first, second); assertThat(actualSum).isEqualByComparingTo(expectedSum); }}

When we run this test method, we notice that the shouldReturnCorrectSum() method is invoked twice by using the parameters which are configured in the data() method.

Even though this approach works, I think that we shouldn’t use it because of the following reasons:

  • It requires a lot of boilerplate code. This makes our test hard to write and read.
  • Because the test data is passed as constructor arguments, we cannot write multiple test methods which use different test data.

Luckily for us, there is a better way. Let’s find out how we can simplify our parameterized tests by using the JUnitParams library.

Writing Parameterized Tests With JUnitParams

Getting the Required Dependencies

Before we can write parameterized tests with JUnitParams, we have to declare the JUnitParams dependency in our build script.

If we are using Maven, we have to add the following snippet into our pom.xml file:

<dependency><groupId>pl.pragmatists</groupId><artifactId>JUnitParams</artifactId><version>1.0.5</version><scope>test</scope></dependency>

If we are using Gradle, we have to add the following snippet into our build.gradle file:

testCompile('pl.pragmatists:JUnitParams:1.0.5')

Let’s move on and write the same unit test by using the JUnitParams library.

Writing the Unit Test

We can ensure that the sum() method of the Calculator class is working as expected by following these steps:

First, we have to ensure that our unit tests are run by the JUnitParamsRunner class. After we have configured the used test runner, the source code of our test class looks as follows:

import junitparams.JUnitParamsRunner;import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;@RunWith(JUnitParamsRunner.class)@Category(UnitTest.class)public class JUnitParamsCalculatorTest {}

Second, we have to write one test method by following these steps:

  1. Add a new test method into our test class. This method takes the provided input and the expected output as method parameters.
  2. Ensure that sum() method of the Calculator class is working as expected.

After we have written this test method, the source code of our test class looks as follows:

import junitparams.JUnitParamsRunner;import org.junit.Test;import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import static org.assertj.core.api.Assertions.assertThat;@RunWith(JUnitParamsRunner.class)@Category(UnitTest.class)public class JUnitParamsCalculatorTest { @Test public void shouldReturnCorrectSum(int first, int second, int expectedSum) { Calculator calculator = new Calculator(); int actualSum = calculator.sum(first, second); assertThat(actualSum).isEqualByComparingTo(expectedSum); }}

Third, we have to configure the parameters that are passed to our test method by following these steps:

  1. Annotate the test method with the @Parameters annotation.
  2. Configure the input provided to the Calculator object and the expected output.

After we have configured the parameters of our parameterized test, the source code of our test class looks as follows:

import junitparams.JUnitParamsRunner;import junitparams.Parameters;import org.junit.Test;import org.junit.experimental.categories.Category;import org.junit.runner.RunWith;import static org.assertj.core.api.Assertions.assertThat;@RunWith(JUnitParamsRunner.class)@Category(UnitTest.class)public class JUnitParamsCalculatorTest { @Test @Parameters({ "0, 0, 0", "1, 1, 2" }) public void shouldReturnCorrectSum(int first, int second, int expectedSum) { Calculator calculator = new Calculator(); int actualSum = calculator.sum(first, second); assertThat(actualSum).isEqualByComparingTo(expectedSum); }}

As we can see, JUnitParams solves the problems which we found from the parameterized tests that use the test runner provided by JUnit. It has no boilerplate code, and because the parameters are passed directly to the test method, we can have multiple test methods that use different parameters. We can even add normal test methods into our test class.

Also, one additional benefit of using JUnitParams is that we can implement custom data providers and read input data from external data sources such as CSV files.

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us four things:

  • We shouldn’t use the test runner provided by JUnit 4 because it forces us to write boilerplate code that makes our tests hard to write and read.
  • If we want to use the JUnitParams library, we have to declare the JUnitParams dependency in our build script.
  • The JUnitParams library solves the problems caused by JUnit 4.
  • The JUnitParams library supports custom data provider classes and external data sources such as CSV files.

Get the source code from Github

Writing Parameterized Tests With JUnit 4 (2025)

FAQs

Does JUnit 4 support parameterized tests? ›

JUnit 4 has introduced a new feature called parameterized tests. Parameterized tests allow a developer to run the same test over and over again using different values. There are five steps that you need to follow to create a parameterized test. Annotate test class with @RunWith(Parameterized.

How to write parameterized JUnit? ›

How do you write a JUnit parameterized test?
  1. Add the junit-jupiter-params dependency to your project.
  2. Add import org. junit. jupiter. params. provider. ...
  3. Replace the @Test annotation of the new method with the @ParameterizedTest one.
  4. Add this new annotation to the method: @ValueSource(ints = {0, -1, 1001})
Mar 31, 2023

Which annotation is used to create a parameterized test class? ›

To create simple unit tests with JUnit we use the @Test annotation. In the case of parameterized tests, we need to use the @ParameterizedTest annotation instead.

What is the difference between test and ParameterizedTest? ›

The main difference is that the values you use in the test come from parameters instead of being hardcoded in the method itself. Another difference is that you usually use a special syntax to express that a given method is a parameterized test and to define the source of the parameterized data.

Is JUnit 4 still used? ›

That said, some teams are still using JUnit 4 and are worried about the disruption that migrating to JUnit 5 could cause. As the first major update since JUnit 4's 2006 release, JUnit 5 dropped in 2017 with a range of new features.

What is the difference between JUnit 4 and JUnit 5 assert? ›

The JUnit 4 primarily relies on static tests means we are unable to test the application in runtime. The JUnit 5 introduces more flexible Assertions class with different methods like assertAll and other supporting multiple Assertions within a single test. JUnit 4 provides basic Assertions by the org. junit.

When not to use parameterized tests? ›

When NOT to use parameterized tests:
  1. When you are testing different functionalities.
  2. When the parameterized test has too many arguments.
  3. When the logic being tested is too complex.
  4. When tests require unique initializations for each scenario.
Jun 13, 2024

How to pass multiple parameters in JUnit? ›

One of the ways to provide parameters to a test case is by using @ValueSource annotation. We can provide multiple values of the same type to a test case with this annotation. The supported types are byte, short, int, long, float, double, char, String, and class.

How to mock a parameter in JUnit? ›

We can use @Mock in our unit tests to create mock objects. On the other hand, we can use @Captor to capture and store arguments passed to mocked methods for later assertions. The introduction of JUnit 5 made it very easy to inject parameters into test methods, making room for this new feature.

Can JUnit run the same test with different inputs? ›

Parameterized testing is a feature of JUnit that allows you to run the same test case with different input parameters. This means that you can test a function or method with multiple sets of input data, which can help you identify edge cases or corner cases that may cause problems in your code.

What is @value in JUnit 5? ›

With the @ValueSource annotation, we can pass an array of literal values to the test method. As we can see, JUnit will run this test two times and each time assigns one argument from the array to the method parameter.

How to pass null in parameterized test? ›

Solution – @NullSource. Since JUnit 5.4 and above, we can use the @NullSource to pass a null value to the parameterized tests.

When to use parameterization? ›

Parameterized tests are used in situations where one needs to perform exactly the same test using a large number of different input values. A good example might be testing a piece of code that performs a calculation, where you have a large collection of known-correct answers that you would like to test.

What is parameterized test suite JUnit 5? ›

Parameterized tests in JUnit 5 offer a powerful way to improve test coverage and efficiency by running the same test logic with various inputs. By using annotations like @ValueSource , @CsvSource , @CsvFileSource , @MethodSource , and @EnumSource , developers can create more concise and maintainable test suites.

What is a parameterized unit test? ›

Parameterized unit tests in Python are a testing technique that allows us to define and execute test cases with different sets of input test data.

What is the test rule in JUnit 4? ›

A JUnit 4 rule is a component that intercepts test method calls and allows us to do something before a test method is run and after a test method has been run. All JUnit 4 rule classes must implement the org. junit.

Is it possible to run JUnit 3 or JUnit 4 tests in JUnit 5? ›

JUnit Jupiter is the combination of the programming model and extension model for writing tests and extensions in JUnit 5. The Jupiter sub-project provides a TestEngine for running Jupiter based tests on the platform. JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform.

Does JUnit 4 run tests in parallel? ›

JUnit 4. Cucumber can be executed in parallel using JUnit and Maven test execution plugins. In JUnit the feature files are run in parallel rather than scenarios, which means all the scenarios in a feature file will be executed by the same thread.

Is it possible to use JUnit 4 and JUnit 5 tests in the same test project or suite )? ›

Junit 4 vs 5 - using both in same project

I prefer to use either JUnit 4 or JUnit 5, but there may be moments when we want to use both. We can do that with JUnit 5 dependencies. Rather than mixing the JUnit 4 and JUnit 5 dependencies, we might be able to get away with the JUnit 5 dependencies alone.

Top Articles
Latest Posts
Recommended Articles
Article information

Author: Allyn Kozey

Last Updated:

Views: 5271

Rating: 4.2 / 5 (63 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Allyn Kozey

Birthday: 1993-12-21

Address: Suite 454 40343 Larson Union, Port Melia, TX 16164

Phone: +2456904400762

Job: Investor Administrator

Hobby: Sketching, Puzzles, Pet, Mountaineering, Skydiving, Dowsing, Sports

Introduction: My name is Allyn Kozey, I am a outstanding, colorful, adventurous, encouraging, zealous, tender, helpful person who loves writing and wants to share my knowledge and understanding with you.