JUnit 5.11 added the @FieldSource annotation for parameterized tests. What does it reference, and when would you prefer it to @MethodSource?
answer
- @FieldSource("CASES") — field instead of factory method
- static unless PER_CLASS; Collection / array / Supplier<Stream>
- Supplier form because a bare Stream field is single-use
- Same-name fallback and FQCN#FIELD external reference
- JUnit 5.11+; shared field = shared mutable elements risk
basics
~20 s@FieldSource names a static field holding the cases — a List, Collection, Stream supplier or array — instead of a factory method. Prefer it when the data is a fixed constant with no logic; a method is still better when the cases must be computed.
solid answer
~50 s`@FieldSource("scenarios")` points at a field rather than a method. The field must be `static` (unless the class uses `@TestInstance(PER_CLASS)`) and hold a `Collection`, `Iterable`, an array, or a `Supplier` of a `Stream`. Element shape follows the usual rule: `Arguments` per element for multi-parameter tests, bare values for single-parameter ones. Like `@MethodSource`, an empty value falls back to a field named after the test method, and an external field can be referenced as `com.acme.Fixtures#CASES`. ```java static final List<String> BLANKS = List.of("", " ", "\t"); @ParameterizedTest @FieldSource("BLANKS") void rejectsBlank(String value) { ... } ``` Use it when the data is a genuine constant — a shared `List` other test code also reads, or a table that reads better as a declared value than as a method returning `Stream.of(...)`. Stick with `@MethodSource` when cases are generated, filtered, loaded, or must be re-created per test. Note the version floor: it does not exist before JUnit 5.11.
code
java · 11 linesstatic final List<Arguments> LENGTH_CASES = List.of(
arguments("", 0),
arguments("abc", 3),
arguments(" ", 2)
);
@ParameterizedTest
@FieldSource("LENGTH_CASES")
void measures(String input, int expected) {
assertEquals(expected, input.length());
}go deeper
Know that it exists and points at a static field of cases instead of a factory method.
State the supported field types including Supplier<Stream>, the static/PER_CLASS rule, and the same-name and FQCN#FIELD forms.
Choose deliberately: constants and shared immutable data as fields, computed or resource-backed data as methods, and flag the shared-mutable-element hazard.
Weigh the version floor for shared modules and set a team convention so fixtures do not drift between two spellings of the same idea.
## What it is `@FieldSource` is an argument source added in **JUnit 5.11** that reads the cases from a **field** instead of from a factory method. It is a small, ergonomic addition: everything you could do with it was already possible with `@MethodSource`, but for constant data the field spelling is more honest. ```java static final List<String> BLANK_VALUES = List.of("", " ", "\t", "\n"); @ParameterizedTest @FieldSource("BLANK_VALUES") void rejectsBlankInput(String value) { assertThrows(IllegalArgumentException.class, () -> Name.of(value)); } ``` ## The rules They mirror `@MethodSource` closely, which is the point: - **Static by default.** Argument resolution happens before test instances exist under the default `PER_METHOD` lifecycle, so the field must be `static`. `@TestInstance(Lifecycle.PER_CLASS)` allows an instance field in the test class itself. - **Supported types:** `Collection` (a `List` or `Set`), other `Iterable`s, arrays, and a `Supplier` of a `Stream` (`Supplier<Stream<Arguments>>`). The `Supplier` form exists because a bare `Stream` field would be consumable exactly once — the supplier lets JUnit obtain a fresh stream, which matters if more than one test references the same field. - **Element shape:** `Arguments` (typically via `Arguments.arguments(...)`) per element for multi-parameter tests; bare values for a single-parameter test. - **Implicit name:** an empty `@FieldSource` falls back to a field whose name matches the test method's name. - **External reference:** `@FieldSource("com.acme.fixtures.Cases#VALID_IBANS")` points at a static field in another class, exactly as `@MethodSource` does for methods. - **Multiple names** may be listed, concatenating their elements. - Visibility does not matter — the engine reads the field reflectively — and `final` is encouraged. ## Why prefer a field **The data is a constant.** A method whose entire body is `return Stream.of(...)` is a method pretending to be data. Declaring `static final List<Arguments> CASES = List.of(arguments(...), ...)` says what it is, and the constant naming convention makes that visible at a glance. **The same constant is used elsewhere.** A `List<String>` of supported currency codes may be consumed by a parameterized test *and* by an assertion in another test *and* by a fixture builder. As a field it is a plain value that anything can read; as a method it needs to be called, and a `Stream`-returning method cannot be reused at all. **Readability of tables.** For a fixed table, the field form removes one layer of syntax (no method signature, no `return`), leaving the rows front and centre. ## Why a method is often still right - **Computation.** Anything filtered, mapped, generated, combined from an enum's values, or loaded from a file belongs in a method. - **Freshness.** A `List` field is created once at class initialisation and shared by every invocation and every test that references it. If elements are **mutable** and a test mutates one, later invocations see the damage — a real hazard when cases carry domain objects. A factory method that constructs new objects each call sidesteps this entirely. Prefer immutable elements in a `@FieldSource` field, or use a `Supplier` that builds fresh values. - **Resources.** `Files.lines(...)` belongs in a method (JUnit closes returned streams); a field holding an open stream is a leak waiting to happen. - **Version floor.** `@FieldSource` does not exist before JUnit 5.11. On an older Jupiter it simply is not on the classpath, and in a library or shared module supporting a range of versions that is a reason to stay with `@MethodSource`. ## The Supplier<Stream<...>> detail Worth knowing because it explains an otherwise odd allowance: ```java static final Supplier<Stream<Arguments>> CASES = () -> Stream.of(arguments("a", 1), arguments("bb", 2)); ``` A `Stream` is single-use. Storing one in a field and referencing it from two tests would fail the second time with `IllegalStateException`. The `Supplier` form gives JUnit a factory for a fresh stream per use — effectively the method form wearing a field's clothes, and the right choice when you want a field but the elements should be produced lazily. ## Interview framing This is a differentiator question, not a screener. A good answer: "`@FieldSource` reads cases from a static field — `Collection`, array, or `Supplier<Stream>` — with the same element rules and same-name fallback as `@MethodSource`, added in 5.11. I use it when the cases are a genuine immutable constant, especially when other code reads the same constant, and stay with `@MethodSource` whenever the data is computed, resource-backed, or mutable." Mentioning the mutability and version-floor caveats is what separates a memorised answer from a used-in-anger one.
- Why does @FieldSource accept a Supplier<Stream<Arguments>> rather than a plain Stream field?A Stream can be consumed only once, so a field holding one would work for the first reference and throw IllegalStateException for any second use — another test, or a re-run within the same class initialisation. The Supplier gives JUnit a way to obtain a fresh stream each time, which also makes the elements lazily produced.
- What is the risk of holding case objects in a shared static @FieldSource field?The elements are created once at class initialisation and shared by every invocation and every test referencing the field. If a test mutates a case object, subsequent invocations see the mutated state and failures become order-dependent. Keep the elements immutable, or use a @MethodSource factory (or Supplier) that constructs fresh objects per use.
saying these in an interview costs you the question
- Assuming @FieldSource exists in JUnit versions before 5.11
- Storing a bare Stream in the field and expecting it to be reusable
- Using an instance field without @TestInstance(PER_CLASS)
- Putting mutable domain objects in a shared field and blaming flakiness on JUnit
- Thinking @FieldSource can compute or filter cases — that needs a method