Appearance
Lesson 02 · Parameterized & Structured Tests
Beyond the 1Z0-830 exam
Once you can write one test, the next skill is writing many without copy-paste, and organizing them so failures are easy to read. JUnit 5's parameterized, nested, dynamic, and tagged tests do exactly that.
Objectives
After this lesson you will be able to:
- Run one test body across many inputs with
@ParameterizedTest+ a source. - Group related tests with
@Nestedand name suites with@DisplayName. - Generate tests at runtime with
@TestFactory(dynamic tests). - Select subsets with
@Tag.
Parameterized tests — one body, many inputs
Replace copy-pasted near-identical tests with a single body fed by a source:
java
@ParameterizedTest(name = "{0} cents @ rate 2.0 -> {1}")
@CsvSource({"0, 0", "50, 100", "1000, 2000"})
void converts(long input, long expected) {
assertThat(svc.quote(input, "X").convertedCents()).isEqualTo(expected);
}Each row runs as a separate, independently reported test case — a failure pinpoints the exact input.
Common sources
| Source | Feeds | Use for |
|---|---|---|
@ValueSource(ints/strings/…) | one argument | a single varying value |
@CsvSource({"a,1", "b,2"}) | multiple args (inline) | small input→expected tables |
@CsvFileSource(resources=…) | multiple args (file) | larger data sets |
@MethodSource("provider") | any args, incl. objects | computed/complex arguments |
@EnumSource(Day.class) | enum constants | every value of an enum |
@NullAndEmptySource | null and empty | boundary inputs for strings/collections |
java
static Stream<Arguments> cases() {
return Stream.of(Arguments.of("EUR", 0.9), Arguments.of("JPY", 150.0));
}
@ParameterizedTest
@MethodSource("cases")
void usesRate(String ccy, double rate) { /* ... */ }Gotcha
@Test and @ParameterizedTest are mutually exclusive — a parameterized method must not also carry @Test, and it must declare at least one source. A parameterized method with no source is a configuration error, not a passing test.
Structuring with @Nested
Group related cases into inner classes for readable, hierarchical reports:
java
@Nested
@DisplayName("isWeekend()")
class WeekendDetection {
@Test void fridayIsWeekday() { assertThat(svc.isWeekend()).isFalse(); }
@Test void saturdayIsWeekend() { assertThat(svc.isWeekend()).isTrue(); }
}Each @Nested class can have its own @BeforeEach, so you set up a shared context for just that group. The report reads like an outline: isWeekend() › Saturday is the weekend.
Dynamic tests — generated at runtime
When the cases aren't known until runtime, @TestFactory returns a collection/stream of DynamicTest:
java
@TestFactory
Stream<DynamicTest> palindromes() {
return Stream.of("level", "noon", "java")
.map(s -> dynamicTest(s, () ->
assertEquals(isPalindrome(s), new StringBuilder(s).reverse().toString().equals(s))));
}Difference from parameterized tests: parameterized cases are declared (annotations, fixed at compile time); dynamic tests are computed at runtime. Reach for dynamic tests only when a parameterized source can't express the cases.
Tags — selecting subsets
@Tag("slow") labels tests so the build can include/exclude them:
java
@Tag("integration")
class DbRoundTripTest { /* ... */ }bash
mvn test -Dgroups=fast # run only @Tag("fast")
mvn test -DexcludedGroups=slow # skip slow onesSDET note
Tags are how Part B's infra-heavy labs (Testcontainers, UI) stay out of the default fast run — a tag plus a Maven profile excludes them so mvn test is quick and green, while CI runs the full set (Modules 15 & 20).
Key Takeaways
@ParameterizedTest+ a source runs one body over many inputs, each reported separately; pick the source (@ValueSource/@CsvSource/@MethodSource/@EnumSource/…) to fit the data.- A parameterized method must not also be
@Testand must declare a source. @Nestedgroups tests into a readable hierarchy, each with its own setup.@TestFactorygenerates tests at runtime when a static source can't.@Tag+ profiles select fast vs slow/integration subsets.
Lesson Quiz
What's the main benefit of @ParameterizedTest?
You need to feed (String, double) pairs computed in code. Which source?
Can a method have both @Test and @ParameterizedTest?
What does @Nested provide?
When are dynamic tests (@TestFactory) the right tool over parameterized tests?
Next: Test Doubles & Mockito.