unit-test-parameterized — Claude Code skill for JUnit 5 tests
A skill that turns one JUnit 5 test method into many runs over different inputs. Useful when you need boundary coverage without copy-pasting assertions.
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a claude-codeWhat unit-test-parameterized does
The skill hands your agent ready patterns for parameterized tests in JUnit 5. It covers @ValueSource for plain values, @CsvSource for tabular rows, @MethodSource for complex objects, @EnumSource for enums, and @ArgumentsSource with a custom ArgumentsProvider. Readable output comes from display names like name = "{0}...".
The workflow it describes: put junit-jupiter on the test classpath, pick a data source, make the method parameters match the source types, set a display name, then run ./gradlew test --info or mvn test. Every parameter set executes as its own test invocation, so the report tells you which case broke.
There are sharp edges. The parameter count from the source has to match the method signature or the test will not start. @ValueSource takes primitives, strings and enums only, not objects or null. Strings containing commas need single quotes inside @CsvSource, and a @MethodSource factory method must be static and live in the same test class. Worth checking your current tests against those rules before converting them.
The skill installs into Claude, Codex, Cursor, GitHub Copilot, Gemini CLI and OpenCode.
What it helps with
- run one check across a dozen input values
- cover boundary cases such as zero and the maximum
- keep a table of email addresses and expected results in a single test
- exercise every enum constant without a method per value
- move test data into a static factory method
Example prompts for the agent
- write a JUnit 5 parameterized test for email validation with @CsvSource
- convert this test to @MethodSource with complex objects
- add a @ParameterizedTest with int boundary values via @ValueSource
How to install unit-test-parameterized
1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a claude-code2Manually: put the files in place
~/.claude/skills/unit-test-parameterized/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/unit-test-parameterized/
1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a codex2Manually: put the files in place
.agents/skills/unit-test-parameterized/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a cursor2Manually: put the files in place
.cursor/skills/unit-test-parameterized/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install giuseppe-trisciuoglio/developer-kit unit-test-parameterized2Manually: put the files in place
.github/skills/unit-test-parameterized/SKILL.md1Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a gemini-cli2Platform CLI
gemini skills install https://github.com/giuseppe-trisciuoglio/developer-kit --path plugins/developer-kit-java/skills/unit-test-parameterizedManually: put the files in place
.devin/skills/unit-test-parameterized/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about unit-test-parameterized
How do I install unit-test-parameterized in Claude?
Run npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-parameterized -a claude-code. The skill file then sits at ~/.claude/skills/unit-test-parameterized/SKILL.md.
What does unit-test-parameterized do?
It guides the agent through writing parameterized JUnit 5 tests: choosing a data source, matching parameter types, and setting readable display names. One method covers many inputs.
What do I need for it to work?
Parameterized tests need junit-jupiter on the test classpath, which already bundles the params module. The examples use assertj-core for assertions.
Source text
SKILL.md · MIT
---
name: unit-test-parameterized
description: Provides parameterized testing patterns with JUnit 5, generates data-driven unit tests using @ParameterizedTest, @ValueSource, @CsvSource, @MethodSource. Creates tests that run the same logic with multiple input values. Use when writing data-driven Java tests, multiple test cases from single method, or boundary value analysis.
allowed-tools: Read, Write, Bash, Glob, Grep
---
# Parameterized Unit Tests with JUnit 5
## Overview
Provides patterns for parameterized unit tests in Java using JUnit 5. Covers `@ValueSource`, `@CsvSource`, `@MethodSource`, `@EnumSource`, `@ArgumentsSource`, and custom display names. Reduces test duplication by running the same test logic with multiple input values.
## When to Use
- Writing JUnit tests with multiple input combinations
- Implementing data-driven tests in Java
- Running same test with different values (boundary analysis)
- Testing multiple scenarios from single test method
## Instructions
1. **Add dependency**: Ensure `junit-jupiter-params` is on test classpath (included in `junit-jupiter`)
2. **Choose source**: `@ValueSource` for simple values, `@CsvSource` for tabular data, `@MethodSource` for complex objects
3. **Match parameters**: Test method parameters must match data source types
4. **Set display names**: Use `name = "{0}..."` for readable output
5. **Validate**: Run `./gradlew test --info` or `mvn test` and verify all parameter combinations execute
## Examples
### Maven / Gradle Dependency
JUnit 5 parameterized tests require `junit-jupiter` (includes params). Add `assertj-core` for assertions:
```xml
<!-- Maven -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
```
```kotlin
// Gradle
testImplementation("org.junit.jupiter:junit-jupiter")
```
### `@ValueSource` — Simple Values
```java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.assertj.core.api.Assertions.*;
@ParameterizedTest
@ValueSource(strings = {"hello", "world", "test"})
void shouldCapitalizeAllStrings(String input) {
assertThat(StringUtils.capitalize(input)).isNotEmpty();
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void shouldBePositive(int number) {
assertThat(number).isPositive();
}
@ParameterizedTest
@ValueSource(ints = {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE})
void shouldHandleBoundaryValues(int value) {
assertThat(Math.incrementExact(value)).isGreaterThan(value);
}
```
### `@CsvSource` — Tabular Data
```java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@ParameterizedTest
@CsvSource({
"[email protected], true",
"[email protected], true",
"invalid-email, false",
"user@, false",
"@example.com, false"
})
void shouldValidateEmailAddresses(String email, boolean expected) {
assertThat(UserValidator.isValidEmail(email)).isEqualTo(expected);
}
```
### `@MethodSource` — Complex Data
```java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
@ParameterizedTest
@MethodSource("additionTestCases")
void shouldAddNumbersCorrectly(int a, int b, int expected) {
assertThat(Calculator.add(a, b)).isEqualTo(expected);
}
static Stream<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)
);
}
```
### `@EnumSource` — Enum Values
```java
@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);
}
```
### Custom Display Names
```java
@ParameterizedTest(name = "Discount of {0}% should be calculated correctly")
@ValueSource(ints = {5, 10, 15, 20})
void shouldApplyDiscount(int discountPercent) {
double result = DiscountCalculator.apply(100.0, discountPercent);
assertThat(result).isEqualTo(100.0 * (1 - discountPercent / 100.0));
}
```
### Custom `ArgumentsProvider`
```java
class RangeValidatorProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of(0, 0, 100, true),
Arguments.of(50, 0, 100, true),
Arguments.of(-1, 0, 100, false),
Arguments.of(101, 0, 100, false)
);
}
}
@ParameterizedTest
@ArgumentsSource(RangeValidatorProvider.class)
void shouldValidateRange(int value, int min, int max, boolean expected) {
assertThat(RangeValidator.isInRange(value, min, max)).isEqualTo(expected);
}
```
### Error Condition Testing
```java
@ParameterizedTest
@ValueSource(strings = {"", " ", null})
void shouldThrowExceptionForInvalidInput(String input) {
assertThatThrownBy(() -> Parser.parse(input))
.isInstanceOf(IllegalArgumentException.class);
}
```
## Best Practices
- Use descriptive display names: `name = "{0}..."` for readable output
- Test boundary values: include min, max, zero, and edge cases
- Keep test logic focused: single assertion per parameter set
- Use `@MethodSource` for complex objects, `@CsvSource` for tabular data
- Organize test data logically — group related scenarios together
## Constraints and Warnings
- **Parameter count must match**: Number of parameters from source must match test method signature
- **`@ValueSource` limitation**: Only supports primitives, strings, and enums — not objects or null directly
- **CSV escaping**: Strings with commas must use single quotes in `@CsvSource`
- **`@MethodSource` visibility**: Factory methods must be static in the same test class
- **Display name placeholders**: Use `{0}`, `{1}`, etc. to reference parameters
- **Execution count**: Each parameter set runs as a separate test invocation
## References
- [JUnit 5 Parameterized Tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests)
- [`@ParameterizedTest` API](https://junit.org/junit5/docs/current/api/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html)
© giuseppe-trisciuoglio · MIT · giuseppe-trisciuoglio/developer-kit