In JUnit 5, how does the @MethodSource annotation supply arguments to a parameterized test, and what happens if you leave the annotation's value empty?
answer
- @MethodSource("factory") → one invocation per element
- Empty value → factory named after the test method
- Stream<Arguments> for many params; bare Stream<T> for one
- arguments(...) / Arguments.of(...) shorthand
- JUnit closes the returned stream after use
basics
~20 s@MethodSource names a factory method that returns a Stream (or Iterable/array) of arguments; JUnit runs the test once per element. If you leave the value empty, JUnit looks for a factory method with the same name as the test method.
solid answer
~50 s`@MethodSource("factoryName")` tells JUnit to call that method, consume what it returns, and invoke the test once per element. For a multi-parameter test, each element is an `Arguments` object — usually built with the static `Arguments.of(...)` (or the shorthand `arguments(...)`): ```java static Stream<Arguments> cases() { return Stream.of(arguments("abc", 3), arguments("", 0)); } ``` For a single-parameter test you can skip `Arguments` entirely and return a `Stream<String>`, a `List<Integer>`, and so on. If the annotation's value is empty — plain `@MethodSource` — JUnit falls back to a factory method **with the same name as the test method**. That is a neat convention for one-off sources but it can confuse readers, so many teams always name the factory explicitly. The point of `@MethodSource` over `@CsvSource` is that arguments are real objects: built with constructors and builders, type-checked by the compiler, not parsed from text.
code
java · 13 lines@ParameterizedTest
@MethodSource("trimCases")
void trims(String input, String expected) {
assertEquals(expected, input.strip());
}
static Stream<Arguments> trimCases() {
return Stream.of(
arguments(" a ", "a"),
arguments("b", "b"),
arguments(" ", "")
);
}go deeper
Write the annotation plus a static factory returning Stream<Arguments> from memory, and state the same-name fallback.
Explain element shapes — Arguments, Object[], bare object for single-parameter tests — and that multiple factory names can be listed.
Argue when a factory beats a CSV table: real objects, compile-time safety, generated or file-loaded cases; mention that JUnit closes the stream.
Position it as the extension point where test data becomes code — shareable, refactor-safe, and therefore subject to the same design pressure as production fixtures.
## Where it fits A JUnit 5 parameterized test is a method annotated `@ParameterizedTest` plus an *argument source* that says where the data comes from. `@ValueSource` and `@CsvSource` hold literal text in the annotation. `@MethodSource` instead names a **factory method** in your own code, so the data can be anything Java can construct. ```java @ParameterizedTest @MethodSource("trimCases") void trims(String input, String expected) { assertEquals(expected, input.strip()); } static Stream<Arguments> trimCases() { return Stream.of( arguments(" a ", "a"), arguments("b", "b"), arguments(" ", "") ); } ``` The engine calls `trimCases()` once, walks the returned stream, and produces one separately reported test invocation per element. ## Elements and parameters How an element maps to the test method's parameters depends on the shape: - **`Arguments`** — the general case. `Arguments.of(a, b, c)` holds one invocation's full argument list, bound positionally. `org.junit.jupiter.params.provider.Arguments.arguments` is a static shorthand usually imported statically for readability. - **`Object[]`** — treated the same way as `Arguments`. - **A bare object** — allowed when the test method takes exactly one parameter. `Stream.of("a", "bb", "ccc")` feeds `void test(String s)` directly, no `Arguments` needed. This is the most common beginner confusion: wrapping single values in `Arguments` is legal but unnecessary, and *forgetting* to wrap when there are two parameters is an error. The argument count must match the parameter count, just as with the CSV sources. ## The implicit-name convention Writing `@MethodSource` with no value makes JUnit look for a factory whose name equals the **test method's** name: ```java @ParameterizedTest @MethodSource void trims(String input, String expected) { } static Stream<Arguments> trims() { ... } ``` This works and is idiomatic in some codebases. Two caveats: it only helps when a factory serves exactly one test, and it makes the link invisible to a casual reader (and to anyone grepping for the factory name). Naming the factory explicitly — `@MethodSource("trimCases")` — costs one string and reads better; both spellings are correct, so have an opinion ready rather than treating one as wrong. You may also list **several** factories: `@MethodSource({"smallCases", "edgeCases"})` concatenates their elements into one run. ## Why choose it over CSV Three reasons come up constantly: 1. **Real objects.** A case can be a fully built domain object, a `Map`, a `List`, a mock, a `Duration` — anything. CSV can only carry text that JUnit knows how to convert. 2. **Compile-time safety.** The factory is ordinary Java: rename a type, change a constructor, and the compiler tells you. A CSV table silently rots. 3. **Computed data.** Cases can be generated — `IntStream.rangeClosed(1, 100).mapToObj(...)` — or loaded from a file, a JSON fixture, or an enum's values. The cost is verbosity: for a table of five literal pairs, `@CsvSource` is shorter and easier to scan. A reasonable rule is *literals go in CSV, objects go in a factory*. ## Streams are closed for you JUnit closes the returned stream after consuming it, so returning `Files.lines(path)` from a factory does not leak a file handle. That also means you must not return a stream you intend to reuse, and you must not return an already-consumed one. ## Reporting Each invocation is reported separately with a display name including the invocation index and the argument values — so a factory producing a hundred cases yields a hundred results, and a failure points at the exact case. Arguments appear via their `toString()`, which is a quiet argument for giving fixture objects a readable `toString()`. ## Practical checklist 1. Annotate the method `@ParameterizedTest` (not `@Test`) and add `@MethodSource`. 2. Return `Stream<Arguments>` for multi-parameter tests; a plain `Stream<T>`/`List<T>` for single-parameter ones. 3. Prefer naming the factory explicitly; use the same-name convention only for one-to-one pairs. 4. Keep the factory close to the test it serves, or move it to a shared class when several tests need it.
- When can a factory return a plain Stream<String> instead of Stream<Arguments>?When the test method declares exactly one parameter. JUnit treats each element as that single argument. With two or more parameters each element must be an `Arguments` (or an `Object[]`), otherwise argument binding fails because one object cannot fill several parameters.
- Can one test use more than one factory method?Yes — `@MethodSource({"smallCases", "edgeCases"})` takes an array of names and concatenates their elements into a single run of invocations, in the order listed. It is a clean way to compose a happy-path set with an edge-case set that other tests also reuse.
saying these in an interview costs you the question
- Leaving @Test on the method so the factory is never consulted
- Thinking an empty @MethodSource means 'no arguments' rather than same-name lookup
- Wrapping single values in Arguments and then declaring two parameters
- Assuming the factory is called once per invocation rather than once in total
- Believing you must close the returned stream yourself