You need a JUnit 5 test to run once per constant of a Java enum, and later to run over only a named subset of those constants. How do you declare that, and how do you express both 'only these' and 'all except these'?
answer
- @EnumSource — one invocation per constant
- class inferable from parameter (5.6+)
- names + mode: INCLUDE default / EXCLUDE
- MATCH_ALL / MATCH_ANY = regex on name()
- INCLUDE/EXCLUDE validate names; regex does not
basics
~20 sUse @ParameterizedTest with @EnumSource. With no attributes it runs over every constant of the enum parameter's type. Restrict with names plus mode: INCLUDE (default) runs only the listed constants, EXCLUDE runs all others, and MATCH_ALL / MATCH_ANY treat the names as regular expressions.
solid answer
~50 s`@EnumSource` supplies enum constants as arguments: ```java @ParameterizedTest @EnumSource(Status.class) // every constant void handlesAll(Status status) { } ``` Since JUnit 5.6 the enum class can be omitted when the test's parameter type is the enum itself — `@EnumSource` alone is enough. Subsetting uses `names` together with `mode`: - `INCLUDE` (default): `@EnumSource(names = {"OPEN", "CLOSED"})` — only those constants. - `EXCLUDE`: `@EnumSource(mode = EXCLUDE, names = {"UNKNOWN"})` — everything else. - `MATCH_ALL` / `MATCH_ANY`: `names` are **regular expressions**, e.g. `mode = MATCH_ALL, names = "^DRAFT_.*"`. The distinction matters for safety. With `INCLUDE`/`EXCLUDE`, a name that matches no constant is a configuration error, so a typo or a renamed constant fails loudly. Regex modes have no such guarantee — a pattern that matches nothing yields zero invocations, which JUnit rejects as an error by default, but a pattern that matches *fewer* constants than intended silently narrows coverage.
code
java · 17 linesenum Status { OPEN, IN_REVIEW, CLOSED, ARCHIVED }
@ParameterizedTest
@EnumSource // type inferred, all constants
void all(Status status) { }
@ParameterizedTest
@EnumSource(names = {"OPEN", "IN_REVIEW"}) // mode = INCLUDE (default)
void onlyLive(Status status) { }
@ParameterizedTest
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "ARCHIVED")
void everythingElse(Status status) { }
@ParameterizedTest
@EnumSource(mode = EnumSource.Mode.MATCH_ALL, names = "^IN_.*$")
void intermediate(Status status) { }go deeper
Show @EnumSource over all constants and the names attribute for a subset; knowing INCLUDE is the default is enough.
Cover all four modes, that regex modes match against name(), and that the enum class can be inferred from the parameter.
Lead with the validation asymmetry — INCLUDE/EXCLUDE fail loudly on renames, regex modes silently narrow — and pick the mode by which failure you want.
Treat enum-driven tests as a coverage contract with the type: choose the mode that makes adding or renaming a constant surface in CI rather than pass quietly.
## Running once per constant `@EnumSource` is the argument source for enums. In its simplest form: ```java enum Status { OPEN, IN_REVIEW, CLOSED, ARCHIVED } @ParameterizedTest @EnumSource void everyStatusIsHandled(Status status) { assertDoesNotThrow(() -> handler.handle(status)); } ``` The engine produces one invocation per declared constant, in declaration order, passing the constant itself. The enum type is taken from the annotation's `value` attribute if given (`@EnumSource(Status.class)`); since JUnit 5.6 it is inferred from the test method's single parameter when omitted. Being explicit is still common in codebases where the parameter is declared as a supertype or where readability matters more than brevity. ## Subsetting: names + mode Two attributes cooperate. `names` is a `String[]`; `mode` is a `Mode` enum that decides how those strings are interpreted: - **`INCLUDE`** — the default. Only constants whose names appear in the list are supplied. - **`EXCLUDE`** — every constant *except* the listed ones. - **`MATCH_ALL`** — names are regular expressions; a constant is selected only if it matches **all** of them. - **`MATCH_ANY`** — names are regular expressions; a constant is selected if it matches **at least one**. - **`MATCH_NONE`** (newer JUnit 5 versions) — the regex complement: constants matching none of the patterns. ```java @ParameterizedTest @EnumSource(mode = EXCLUDE, names = {"ARCHIVED"}) void liveStatusesAreEditable(Status status) { } @ParameterizedTest @EnumSource(mode = MATCH_ALL, names = "^IN_.*$") void intermediateStatuses(Status status) { } ``` Regex modes match against the constant's `name()`, and the whole name must match the pattern. ## The validation difference that matters With `INCLUDE` and `EXCLUDE`, JUnit validates the supplied names against the enum's actual constants. A name that does not exist — a typo, or a constant someone renamed — makes the test fail with a configuration error naming the bad entry. That is a genuinely useful property: refactoring the enum cannot silently detach the test from it. Regex modes give up that safety. Patterns are not validated against anything; they simply select whatever they select. If a rename turns `IN_REVIEW` into `UNDER_REVIEW`, a `^IN_.*` pattern quietly stops matching it. When nothing at all matches, the test produces zero invocations, which JUnit treats as an error by default (recent versions expose an `allowZeroInvocations` opt-out on `@ParameterizedTest`) — but partial silent narrowing is the real hazard, and it produces a green build with reduced coverage. So: prefer `INCLUDE`/`EXCLUDE` for small, explicit sets; reserve regex modes for enums with a genuine naming convention (`ERROR_*`, `*_DEPRECATED`) where the convention is itself the selection criterion. ## Reporting Invocation labels render the constant via `toString()`, so with default settings a run reads `[2] status=IN_REVIEW`. That readability is part of why `@EnumSource` is preferred over listing constant names as strings in a literal source: the argument is the real enum value, type-checked at compile time and renamed automatically by IDE refactoring, rather than a string that drifts. ## Where it fits `@EnumSource` covers the 'same behaviour for each constant' shape. As soon as each constant needs a *different* expected result, you are back to argument tuples and a different source — pairing a constant with its expectation is not something `@EnumSource` can express, and forcing it leads to a `switch` inside the test body, which is a test that asserts its own copy of the production mapping. ## How to answer Show the bare form, then `names` + the four modes, and volunteer the validation asymmetry between the literal modes and the regex modes. That last point is the one that distinguishes usage from understanding.
- What happens if a name listed under mode = INCLUDE does not correspond to any constant of the enum?JUnit fails the test with a configuration error identifying the invalid name. INCLUDE and EXCLUDE validate their names against the enum's declared constants, so a typo or a renamed constant is caught loudly. Regex modes perform no such validation and would simply select nothing.
- Each enum constant needs a different expected outcome. Is @EnumSource still the right source?No. @EnumSource supplies one argument — the constant — and cannot pair it with an expectation. Encoding the expectation with a switch inside the test body just duplicates the production mapping in the test. Move to a source that yields argument tuples so constant and expected value travel together.
saying these in an interview costs you the question
- Believing names is always a regex list, regardless of mode.
- Thinking EXCLUDE is the default when names is present (INCLUDE is).
- Assuming a regex that matches nothing is reported as a failure of intent — it is only caught because zero invocations is an error.
- Claiming the enum class attribute is always mandatory (inferable from the parameter since 5.6).
- Putting a switch on the constant inside the test body and calling it parameterized.