Unit test parameterized
Skill ulpi-io/plugin-marketplace/plugins/giuseppe-trisciuoglio/skills/unit-test-parameterized
A curated collection of 7,800+ agent skills for Claude Desktop, sourced from skills.sh
npx -y skills add ulpi-io/plugin-marketplace --skill unit-test-parameterizedAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Provides parameterized testing patterns with @ParameterizedTest, @ValueSource, @CsvSource. Enables running a single test method with multiple input combinations. Use when testing multiple scenarios with similar logic.
SKILL.md
11.8 KB, as published. Nobody here has run it
Parameterized Unit Tests with JUnit 5
Overview
This skill provides patterns for writing efficient parameterized unit tests using JUnit 5's @ParameterizedTest. It covers @ValueSource, @CsvSource, @MethodSource, @EnumSource, @ArgumentsSource, and custom display names to run the same test logic with multiple input values, reducing test duplication and improving coverage.
When to Use
Use this skill when:
- Testing methods with multiple valid inputs
- Testing boundary values systematically
- Testing multiple invalid inputs for error cases
- Want to reduce test duplication
- Testing multiple scenarios with similar assertions
- Need data-driven testing approach
Instructions
- Add junit-jupiter-params dependency: Ensure junit-jupiter-params is on test classpath
- Choose appropriate source: Use @ValueSource for simple values, @CsvSource for tabular data, @MethodSource for complex objects
- Match parameter types: Ensure test method parameters match data source types
- Use descriptive display names: Set
name = "..."for readable test output - Test boundary values: Include edge cases, null values, and extreme values in parameters
- Use @EnumSource: Test all enum values or filter specific ones
- Create custom ArgumentsProvider: Build reusable data sources for complex scenarios
- Keep assertions simple: Focus on single assertion per parameterized test
Examples
Setup: Parameterized Testing
Maven
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
Gradle
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.assertj:assertj-core")
}
Basic Pattern: @ValueSource
Simple Value Testing
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.assertj.core.api.Assertions.*;
class StringUtilsTest {
@ParameterizedTest
@ValueSource(strings = {"hello", "world", "test"})
void shouldCapitalizeAllStrings(String input) {
String result = StringUtils.capitalize(input);
assertThat(result).startsWith(input.substring(0, 1).toUpperCase());
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void shouldBePositive(int number) {
assertThat(number).isPositive();
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void shouldHandleBothBooleanValues(boolean value) {
assertThat(value).isNotNull();
}
}
@MethodSource for Complex Data
Factory Method Data Source
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
class CalculatorTest {
static Stream<org.junit.jupiter.params.provider.Arguments> additionTestCases() {
return Stream.of(
Arguments.of(1, 2, 3),
Arguments.of(0, 0, 0),
Arguments.of(-1, 1, 0),
Arguments.of(100, 200, 300),
Arguments.of(-5, -10, -15)
);
}
@ParameterizedTest
@MethodSource("additionTestCases")
void shouldAddNumbersCorrectly(int a, int b, int expected) {
int result = Calculator.add(a, b);
assertThat(result).isEqualTo(expected);
}
}
@CsvSource for Tabular Data
CSV-Based Test Data
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class UserValidationTest {
@ParameterizedTest
@CsvSource({
"[email protected], true",
"[email protected], true",
"invalid-email, false",
"user@, false",
"@example.com, false",
"user [email protected], false"
})
void shouldValidateEmailAddresses(String email, boolean expected) {
boolean result = UserValidator.isValidEmail(email);
assertThat(result).isEqualTo(expected);
}
@ParameterizedTest
@CsvSource({
"123-456-7890, true",
"555-123-4567, true",
"1234567890, false",
"123-45-6789, false",
"abc-def-ghij, false"
})
void shouldValidatePhoneNumbers(String phone, boolean expected) {
boolean result = PhoneValidator.isValid(phone);
assertThat(result).isEqualTo(expected);
}
}
@CsvFileSource for External Data
CSV File-Based Testing
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
class PriceCalculationTest {
@ParameterizedTest
@CsvFileSource(resources = "/test-data/prices.csv", numLinesToSkip = 1)
void shouldCalculateTotalPrice(String product, double price, int quantity, double expected) {
double total = PriceCalculator.calculateTotal(price, quantity);
assertThat(total).isEqualTo(expected);
}
}
// test-data/prices.csv:
// product,price,quantity,expected
// Laptop,999.99,1,999.99
// Mouse,29.99,3,89.97
// Keyboard,79.99,2,159.98
@EnumSource for Enum Testing
Enum-Based Test Data
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
enum Status { ACTIVE, INACTIVE, PENDING, DELETED }
class StatusHandlerTest {
@ParameterizedTest
@EnumSource(Status.class)
void shouldHandleAllStatuses(Status status) {
assertThat(status).isNotNull();
}
@ParameterizedTest
@EnumSource(value = Status.class, names = {"ACTIVE", "INACTIVE"})
void shouldHandleSpecificStatuses(Status status) {
assertThat(status).isIn(Status.ACTIVE, Status.INACTIVE);
}
@ParameterizedTest
@EnumSource(value = Status.class, mode = EnumSource.Mode.EXCLUDE, names = {"DELETED"})
void shouldHandleStatusesExcludingDeleted(Status status) {
assertThat(status).isNotEqualTo(Status.DELETED);
}
}
Custom Display Names
Readable Test Output
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class DiscountCalculationTest {
@ParameterizedTest(name = "Discount of {0}% should be calculated correctly")
@ValueSource(ints = {5, 10, 15, 20})
void shouldApplyDiscount(int discountPercent) {
double originalPrice = 100.0;
double discounted = DiscountCalculator.apply(originalPrice, discountPercent);
double expected = originalPrice * (1 - discountPercent / 100.0);
assertThat(discounted).isEqualTo(expected);
}
@ParameterizedTest(name = "User role {0} should have {1} permissions")
@CsvSource({
"ADMIN, 100",
"MANAGER, 50",
"USER, 10"
})
void shouldHaveCorrectPermissions(String role, int expectedPermissions) {
User user = new User(role);
assertThat(user.getPermissionCount()).isEqualTo(expectedPermissions);
}
}
Combining Multiple Sources
ArgumentsProvider for Complex Scenarios
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import java.util.stream.Stream;
class RangeValidatorArgumentProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of(0, 0, 100, true), // Min boundary
Arguments.of(100, 0, 100, true), // Max boundary
Arguments.of(50, 0, 100, true), // Middle value
Arguments.of(-1, 0, 100, false), // Below range
Arguments.of(101, 0, 100, false) // Above range
);
}
}
class RangeValidatorTest {
@ParameterizedTest
@ArgumentsSource(RangeValidatorArgumentProvider.class)
void shouldValidateRangeCorrectly(int value, int min, int max, boolean expected) {
boolean result = RangeValidator.isInRange(value, min, max);
assertThat(result).isEqualTo(expected);
}
}
Testing Edge Cases with Parameters
Boundary Value Analysis
class BoundaryValueTest {
@ParameterizedTest
@ValueSource(ints = {
Integer.MIN_VALUE, // Absolute minimum
Integer.MIN_VALUE + 1, // Just above minimum
-1, // Negative boundary
0, // Zero boundary
1, // Just above zero
Integer.MAX_VALUE - 1, // Just below maximum
Integer.MAX_VALUE // Absolute maximum
})
void shouldHandleAllBoundaryValues(int value) {
int incremented = MathUtils.increment(value);
assertThat(incremented).isNotLessThan(value);
}
@ParameterizedTest
@CsvSource({
", false", // null
"'', false", // empty
"' ', false", // whitespace only
"a, true", // single character
"abc, true" // normal
})
void shouldValidateStrings(String input, boolean expected) {
boolean result = StringValidator.isValid(input);
assertThat(result).isEqualTo(expected);
}
}
Repeat Tests
Run Same Test Multiple Times
import org.junit.jupiter.api.RepeatedTest;
class ConcurrencyTest {
@RepeatedTest(100)
void shouldHandleConcurrentAccess() {
// Test that might reveal race conditions if run multiple times
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
assertThat(counter.get()).isEqualTo(1);
}
}
Best Practices
- Use @ParameterizedTest to reduce test duplication
- Use descriptive display names with
(name = "...") - Test boundary values systematically
- Keep test logic simple - focus on single assertion
- Organize test data logically - group similar scenarios
- Use @MethodSource for complex test data
- Use @CsvSource for tabular test data
- Document expected behavior in test names
Common Patterns
Testing error conditions:
@ParameterizedTest
@ValueSource(strings = {"", " ", null})
void shouldThrowExceptionForInvalidInput(String input) {
assertThatThrownBy(() -> Parser.parse(input))
.isInstanceOf(IllegalArgumentException.class);
}
Testing multiple valid inputs:
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 5, 8, 13})
void shouldBeInFibonacciSequence(int number) {
assertThat(FibonacciChecker.isFibonacci(number)).isTrue();
}
Constraints and Warnings
- Parameter count must match: The number of parameters from source must match test method signature
- Type conversion is automatic: JUnit converts source values to target parameter types when possible
- @ValueSource limitation: Only supports literals (strings, ints, longs, doubles); not objects or null
- CSV escaping: Strings containing commas must be enclosed in single quotes in @CsvSource
- MethodSource visibility: @MethodSource methods must be static, can be private but must be in same class
- Display name placeholders: Use {0}, {1}, etc. to reference parameters in display names
- Test execution order: Parameterized tests execute each parameter set as a separate test invocation
Troubleshooting
Parameter not matching: Verify number and type of parameters match test method signature.
Display name not showing: Check parameter syntax in name = "...".
CSV parsing error: Ensure CSV format is correct and quote strings containing commas.