# Parameterized Tests — JUnit 5 & Mockito

Source: https://www.skillbyai.com/en/java-testing/j-parameterized

> Run one test with many inputs using value, CSV, enum and method sources.

## One test, many cases

When the same behaviour must hold for many inputs, **`@ParameterizedTest`** runs one test method once per set of arguments, reporting each case separately. Sources supply the arguments. **`@ValueSource`** provides a single literal per invocation (ints, strings and so on). **`@CsvSource`** provides several columns per row inline (`"99999, true, 9900"`), and **`@CsvFileSource`** reads them from a file. **`@EnumSource`** runs once per enum constant, optionally filtered. **`@MethodSource`** refers to a static factory method returning a `Stream<Arguments>` for complex objects. **`@NullSource`**, **`@EmptySource`** and **`@NullAndEmptySource`** add edge cases. Customise readable case names with `name = "{index}: total {0} -> fee {2}"`. Parameterised tests are ideal for **boundary values** (just below, at and just above a threshold), validation rules and conversions, and they replace copy-pasted tests that differ only in their data.

## Boundary values with CSV and method sources

Each row is reported as its own test case.

```java
class ShippingCalculatorParamTest {
    private final ShippingCalculator calc = new ShippingCalculator();

    @ParameterizedTest(name = "total {0}, express {1} -> fee {2}")
    @CsvSource({
        "0,       false, 4900",
        "99999,   false, 4900",
        "99999,   true,  9900",
        "100000,  false, 0",
        "100000,  true,  0"
    })
    void feeDependsOnTotalAndSpeed(long totalPaise, boolean express, long expectedFee) {
        assertEquals(expectedFee, calc.feePaise(totalPaise, express));
    }

    @ParameterizedTest
    @NullAndEmptySource
    @ValueSource(strings = {"  ", "not-an-email", "a@"})
    void invalidEmailsAreRejected(String email) {
        assertFalse(EmailValidator.isValid(email));
    }

    static Stream<Arguments> coupons() {
        return Stream.of(
            Arguments.of("FESTIVE10", 100_000, 10_000),
            Arguments.of("FLAT50",    20_000,  5_000));
    }

    @ParameterizedTest
    @MethodSource("coupons")
    void couponDiscounts(String code, long total, long expectedDiscount) {
        assertEquals(expectedDiscount, Coupons.discountPaise(code, total));
    }
}
```

## Test the boundaries

Bugs cluster at edges: 99,999 versus 100,000 paise, empty strings, zero quantities, the last day of the month. Parameterised tests make it cheap to cover each edge explicitly.

**Quiz:** Which source runs a parameterized test once per row of inline comma-separated values?

- [x] @CsvSource
- [ ] @ValueSource
- [ ] @EnumSource
- [ ] @NullSource

*Answer:* @CsvSource. @CsvSource supplies multiple arguments per invocation from inline CSV rows.
