JUnit – Parameterized Tests
In this JUnit tutorial, we’ll explore the basics of parameterized tests, their setup, and how to use different sources to supply test data.
What are Parameterized Tests?
Parameterized tests allow a single test method to execute multiple times with different sets of parameters. This eliminates repetitive test cases and makes tests more concise and maintainable.
- Each test run is executed with a unique combination of input values.
- Results are evaluated for every set of inputs.
- JUnit 5 provides built-in annotations and methods to define parameterized tests.
Setting Up Parameterized Tests
To use parameterized tests, ensure your project is configured with JUnit 5 (JUnit Jupiter). Add the following dependency to your pom.xml
:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
Once this dependency is added, you can begin writing parameterized tests.
Annotations for Parameterized Tests
JUnit 5 provides several annotations to supply parameters to tests:
@ParameterizedTest
: Marks a test as parameterized.@ValueSource
: Supplies a single array of values to the test.@CsvSource
: Supplies multiple sets of values separated by commas.@CsvFileSource
: Reads test data from a CSV file.@MethodSource
: Supplies test data from a static method.
Writing a Parameterized Test
1 Using @ValueSource
The @ValueSource
annotation provides a single array of primitive or String values to a parameterized test. Here’s an example:
src/test/java/ValueSourceTest.java
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class ValueSourceTest {
@ParameterizedTest
@ValueSource(ints = {1, 3, 9, 171, 15})
void isOdd_ShouldReturnTrueForOddNumbers(int number) {
assertTrue(number % 2 != 0, "Number should be odd");
}
}
In this test, the method is executed five times, each with a different value from the ints
array as shown in the following video.
2 Using @CsvSource
The @CsvSource
annotation provides multiple sets of data in CSV format. This is useful for testing methods with multiple arguments.
src/test/java/CsvSourceTest.java
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class CsvSourceTest {
@ParameterizedTest
@CsvSource({
"2, 3, 5",
"1, 1, 2",
"4, 5, 9"
})
void testAddition(int a, int b, int expected) {
assertEquals(expected, a + b, () -> a + " + " + b + " should equal " + expected);
}
}
Here, the test runs three times, each with a different set of values from the @CsvSource
as shown in the following video.
3 Using @CsvFileSource
Use @CsvFileSource
to load test data from an external CSV file. This is helpful for managing large data sets.
Steps to Add the CSV File:
- Navigate to the
src/test/resources
directory in your project. - Create a file named
test-data.csv
. - Add the necessary data to the file, for example.
src/test/resources/test-data.csv
a,b,expected
2,3,5
4,5,9
1,1,2
src/test/java/CsvFileSourceTest.java
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
class CsvFileSourceTest {
@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv", numLinesToSkip = 1)
void testFromCsvFile(int a, int b, int expected) {
assertEquals(expected, a + b);
}
}
The resources
attribute specifies the file path, and numLinesToSkip=1
skips the header row.
When you run this program, the test runs three times, each with a different set of values from the @CsvFileSource
as shown in the following video.
4 Using @MethodSource
The @MethodSource
annotation allows you to supply parameters from a static method:
src/test/java/MethodSourceTest.java
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
class MethodSourceTest {
static Stream<Arguments> provideAdditionData() {
return Stream.of(
Arguments.of(2, 3, 5),
Arguments.of(1, 1, 2),
Arguments.of(4, 5, 9)
);
}
@ParameterizedTest
@MethodSource("provideAdditionData")
void testWithMethodSource(int a, int b, int expected) {
assertEquals(expected, a + b);
}
}
The method provideAdditionData
supplies data as a stream of arguments for the test.
When you run this program, the test runs three times, each with a different set of values from the @MethodSource
as shown in the following video.
Benefits of Parameterized Tests
- Efficiency: Reduce repetitive code by testing multiple scenarios in a single test.
- Readability: Test cases are cleaner and easier to understand.
- Scalability: Add more test data without modifying test logic.
Best Practices
- Ensure test data covers all edge cases.
- Use meaningful names for test methods and data providers.
- Organize large datasets using
@CsvFileSource
or external files. - Keep tests focused on specific behaviors for clarity.
Conclusion
Parameterized tests are a powerful feature of JUnit that simplify testing multiple inputs and scenarios. By using annotations like @ValueSource
, @CsvSource
, and @MethodSource
, you can create concise, efficient, and maintainable tests that improve the overall quality of your codebase.