skip to content

@MethodSource & @ArgumentsSource

Factory-method and provider-class sources for complex arguments. Asked to see if you know the static-method rules, Arguments.of, and when to write a reusable ArgumentsProvider.

on this pageshow

questions

5

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?

level: juniorimportance: must knowfreq 58%

answer

  1. @MethodSource("factory") → one invocation per element
  2. Empty value → factory named after the test method
  3. Stream<Arguments> for many params; bare Stream<T> for one
  4. arguments(...) / Arguments.of(...) shorthand
  5. 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
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("   ", "")
    );
}

go deeper

for a junior

Write the annotation plus a static factory returning Stream<Arguments> from memory, and state the same-name fallback.

for a middle

Explain element shapes — Arguments, Object[], bare object for single-parameter tests — and that multiple factory names can be listed.

for a senior

Argue when a factory beats a CSV table: real objects, compile-time safety, generated or file-loaded cases; mention that JUnit closes the stream.

for a principal

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

context

open as a page

What rules must a JUnit 5 @MethodSource factory method satisfy — must it be static, which return types are accepted — and how do you supply several arguments per invocation versus a single one?

level: middleimportance: must knowfreq 50%

basics

~20 s

The factory must be static unless the test class is annotated @TestInstance(PER_CLASS). It may return Stream (including IntStream/LongStream/DoubleStream), Iterable, Iterator, Collection or an array. Each element is an Arguments/Object[] for multi-parameter tests, or a bare value for a single-parameter test.

open as a page

How do you point a JUnit 5 @MethodSource at a factory method that lives in a different class, and why would you keep shared test data there rather than duplicating it in each test class?

level: middleimportance: should knowfreq 36%

basics

~10 s

Give the fully qualified class name, then #, then the method name: @MethodSource("com.acme.TestData#validEmails"). The external method must be static. It lets several test classes share one authoritative set of cases instead of copying rows.

open as a page

When would you implement a custom JUnit 5 ArgumentsProvider and wire it with @ArgumentsSource instead of using @MethodSource, and how do you make such a provider configurable through your own annotation?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Write an ArgumentsProvider when the data source is reusable logic rather than a list — generated, loaded from files, or driven by ExtensionContext. Wire it with @ArgumentsSource(MyProvider.class). Make it configurable by having the provider implement AnnotationConsumer<MyAnnotation> and putting @ArgumentsSource on your own annotation.

open as a page

JUnit 5.11 added the @FieldSource annotation for parameterized tests. What does it reference, and when would you prefer it to @MethodSource?

level: middleimportance: nice to knowfreq 16%

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.

open as a page