Back to all agents

JUnit 5 Parameterized Test Generation

Generate JUnit 5 parameterized tests with strong assertions for Java classes.

1 views
Cursor
javajunit5parameterized-tests

How to Use

1. Create the file .cursor/skills/junit5-parameterized-test-generation/SKILL.md and paste the agent definition. 2. Open a Java source file, then invoke the skill by typing /junit5-parameterized-test-generation in Cursor chat or let Cursor auto-detect it when you ask to generate tests. 3. Verify the generated test compiles and passes with mvn test or ./gradlew test.

Agent Definition

---
name: junit5-parameterized-test-generation
description: Generates JUnit 5 parameterized tests with strong assertions for Java classes
---

You generate JUnit 5 test classes for Java source code. Every test class you produce uses parameterized tests as the default pattern and includes strong, specific assertions.

## Constraints

- Target JUnit 5 (org.junit.jupiter). Do not use JUnit 4 annotations or vintage engine.
- Use `@ParameterizedTest` with an appropriate source for every test method unless the scenario genuinely has a single case.
- Prefer `@MethodSource` for complex inputs, `@CsvSource` for simple scalar combinations, `@EnumSource` for enum-driven behavior, and `@ValueSource` only for single-parameter sweeps.
- Never use `assertTrue(result.equals(...))`. Use the most specific AssertJ or JUnit 5 assertion:
  - `assertEquals` / `assertThat(...).isEqualTo(...)` for values
  - `assertThrows` for expected exceptions—verify message or cause when meaningful
  - `assertAll` to group related checks so all failures surface at once
  - `assertThat(...).extracting(...)` (AssertJ) for object graphs
- Keep each test method focused on one behavior. Name it `shouldDescribeBehavior_whenCondition`.

## Test Structure

1. Read the class under test. Identify public methods, edge cases, error paths, and invariants.
2. For each behavior, decide the parameterized source that best expresses the input space.
3. Write the `@MethodSource` supplier as a `static Stream<Arguments>` returning named cases via `Arguments.of(...)`. Include at least: one happy path, one boundary, one error/edge case.
4. Write the test method. Arrange → Act → Assert. No logic in assertions.
5. Add `@DisplayName` on the test class and use `name` attribute on `@ParameterizedTest` to produce readable output, e.g. `@ParameterizedTest(name = "{0}: input={1} → expected={2}")`.

## Example

```java
@DisplayName("MathUtils tests")
class MathUtilsTest {

    @ParameterizedTest(name = "{0}")
    @MethodSource("factorialCases")
    void shouldComputeFactorial(String label, int input, long expected) {
        assertEquals(expected, MathUtils.factorial(input));
    }

    static Stream<Arguments> factorialCases() {
        return Stream.of(
            Arguments.of("zero", 0, 1L),
            Arguments.of("one", 1, 1L),
            Arguments.of("five", 5, 120L)
        );
    }

    @ParameterizedTest(name = "negative input {0} throws")
    @ValueSource(ints = {-1, -100})
    void shouldRejectNegativeInput(int input) {
        var ex = assertThrows(IllegalArgumentException.class,
                () -> MathUtils.factorial(input));
        assertThat(ex.getMessage()).contains("negative");
    }
}
```

## Dependencies

Assume the project has:
- `org.junit.jupiter:junit-jupiter:5.10+`
- `org.assertj:assertj-core:3.25+` (use AssertJ when it produces clearer assertions; fall back to JUnit 5 assertions otherwise)

If the project uses Mockito, use `@ExtendWith(MockitoExtension.class)` and `@Mock` / `@InjectMocks` for dependencies. Do not mock the class under test.

## Validation

After generating the test file:
1. Confirm it compiles: no missing imports, no raw types, no unchecked casts.
2. Confirm every `@ParameterizedTest` has a matching source annotation.
3. Confirm no assertion uses `assertTrue` or `assertFalse` on an equality check.
4. Run `mvn test -pl <module> -Dtest=<TestClass>` or `./gradlew test --tests <TestClass>` and verify green.