Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/giuseppe-trisciuoglio-unit-test-parameterized/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-20 | ✓→✗ | ▼ Worse | 138% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 67% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 90% | 0% |
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.
junit-jupiter-params is on test classpath (included in junit-jupiter)@ValueSource for simple values, @CsvSource for tabular data, @MethodSource for complex objectsname = "{0}..." for readable output./gradlew test --info or mvn test and verify all parameter combinations executeJUnit 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 Valuesjavaimport 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 Datajavaimport org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @ParameterizedTest @CsvSource({ "alice@example.com, true", "bob@gmail.com, true", "invalid-email, false", "user@, false", "@example.com, false" }) void shouldValidateEmailAddresses(String email, boolean expected) { assertThat(UserValidator.isValidEmail(email)).isEqualTo(expected); }
@MethodSource — Complex Datajavaimport 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 Valuesjava@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); }
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)); }
ArgumentsProviderjavaclass 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); }
java@ParameterizedTest @ValueSource(strings = {"", " ", null}) void shouldThrowExceptionForInvalidInput(String input) { assertThatThrownBy(() -> Parser.parse(input)) .isInstanceOf(IllegalArgumentException.class); }
name = "{0}..." for readable output@MethodSource for complex objects, @CsvSource for tabular data@ValueSource limitation: Only supports primitives, strings, and enums — not objects or null directly@CsvSource@MethodSource visibility: Factory methods must be static in the same test class{0}, {1}, etc. to reference parameters| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 8,030 | 5,642 | -30% | 1 | 1 | 0% | 1,428 | 2,611 | +83% | 0 | 0 | — |
case-02 | fail→fail | 11,194 | 9,785 | -13% | 1 | 1 | 0% | 2,220 | 3,627 | +63% | 0 | 0 | — |
case-03 | pass→pass | 8,937 | 5,243 | -41% | 1 | 1 | 0% | 1,524 | 2,549 | +67% | 0 | 0 | — |
case-04 | pass→pass | 9,164 | 9,758 | +6% | 1 | 1 | 0% | 1,667 | 3,171 | +90% | 0 | 0 | — |
case-05 | pass→pass | 6,179 | 5,482 | -11% | 1 | 1 | 0% | 1,197 | 2,664 | +123% | 0 | 0 | — |
case-06 | pass→pass | 4,943 | 3,902 | -21% | 1 | 1 | 0% | 788 | 2,275 | +189% | 0 | 0 | — |
case-07 | pass→pass | 5,394 | 4,627 | -14% | 1 | 1 | 0% | 954 | 2,345 | +146% | 0 | 0 | — |
case-08 | pass→pass | 7,095 | 4,604 | -35% | 1 | 1 | 0% | 1,357 | 2,504 | +85% | 0 | 0 | — |
case-09 | pass→pass | 5,652 | 3,147 | -44% | 1 | 1 | 0% | 1,156 | 2,193 | +90% | 0 | 0 | — |
case-10 | pass→pass | 12,773 | 11,914 | -7% | 1 | 1 | 0% | 2,530 | 4,254 | +68% | 0 | 0 | — |
case-11 | fail→pass | 7,177 | 7,544 | +5% | 1 | 1 | 0% | 1,400 | 2,829 | +102% | 0 | 0 | — |
case-12 | pass→pass | 2,606 | 2,971 | +14% | 1 | 1 | 0% | 446 | 2,038 | +357% | 0 | 0 | — |
case-13 | pass→pass | 14,345 | 6,756 | -53% | 1 | 1 | 0% | 2,212 | 2,921 | +32% | 0 | 0 | — |
case-14 | pass→pass | 6,451 | 4,128 | -36% | 1 | 1 | 0% | 1,200 | 2,331 | +94% | 0 | 0 | — |
case-15 | pass→pass | 8,778 | 3,677 | -58% | 1 | 1 | 0% | 1,345 | 2,217 | +65% | 0 | 0 | — |
case-16 | pass→pass | 10,155 | 8,169 | -20% | 1 | 1 | 0% | 1,851 | 2,927 | +58% | 0 | 0 | — |
case-17 | pass→pass | 5,114 | 4,106 | -20% | 1 | 1 | 0% | 790 | 2,287 | +189% | 0 | 0 | — |
case-18 | pass→pass | 13,813 | 12,782 | -7% | 1 | 1 | 0% | 2,211 | 3,669 | +66% | 0 | 0 | — |
case-19 | pass→pass | 12,557 | 8,336 | -34% | 1 | 1 | 0% | 2,117 | 3,014 | +42% | 0 | 0 | — |
case-20 | pass→fail | 7,641 | 35,527 | +365% | 1 | 1 | 0% | 1,341 | 3,197 | +138% | 0 | 0 | — |
case-21 | pass→pass | 11,687 | 12,133 | +4% | 1 | 1 | 0% | 2,209 | 3,877 | +76% | 0 | 0 | — |
case-22 | pass→pass | 8,468 | 6,478 | -24% | 1 | 1 | 0% | 1,749 | 2,825 | +62% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.