What is @MethodSource, when is it the right choice, and what are the rules for the factory method it points to?
answer
- Factory method returning Stream<Arguments>
- Arguments.of(...) packs multiple params
- static unless @TestInstance(PER_CLASS)
- Class#method to share across classes; empty = same name
- Use it for real objects / computed data
basics
~20 s@MethodSource names a method that returns the test data, usually a Stream of Arguments. Use it when the inputs are real objects or computed at runtime — things you can't write as plain literals in @ValueSource or @CsvSource.
solid answer
~50 s@MethodSource points the parameterized test at a **factory method** that returns the arguments — typically a `Stream<Arguments>` (also `Collection`, `Iterator`, or an array). Each element supplies one invocation; `Arguments.of(a, b, ...)` packs the multiple parameters for a row. By convention the factory is a `static` method in the same class with the same name as the value you pass (or you give a fully-qualified `Class#method` to reuse it across classes). It must be static unless the test class uses `@TestInstance(Lifecycle.PER_CLASS)`, and it takes no arguments (it can resolve some via ParameterResolver). You reach for @MethodSource when the data is **not expressible as literals**: real domain objects, values built from a fixture, large or programmatically generated tables, or cases shared across test classes. It is the most flexible source short of a custom @ArgumentsSource, at the cost of being less declarative than @CsvSource.
code
java · 33 linesimport java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.params.provider.Arguments.arguments;
class DiscountTest {
@ParameterizedTest(name = "{0} => {1}")
@MethodSource("discountCases")
void appliesDiscount(Order order, int expectedCents) {
assertEquals(expectedCents, order.totalAfterDiscount());
}
static Stream<Arguments> discountCases() {
return Stream.of(
arguments(new Order("VIP", 1000), 800),
arguments(new Order("NEW", 1000), 900),
arguments(new Order("NONE", 1000), 1000)
);
}
record Order(String tier, int cents) {
int totalAfterDiscount() {
return switch (tier) {
case "VIP" -> (int) (cents * 0.8);
case "NEW" -> (int) (cents * 0.9);
default -> cents;
};
}
}
}go deeper
Knows @MethodSource names a method that returns the test data instead of inline literals.
Writes a static factory returning Stream<Arguments> with Arguments.of(...), and chooses it when inputs are objects or computed.
Understands name resolution (same-class, empty=same-name, Class#method), the PER_CLASS static exception, and trade-offs vs @CsvSource and @ArgumentsSource.
Designs shared, maintainable data providers across the suite, manages display-name readability for object arguments, and decides when to graduate to @ArgumentsSource or property-based testing.
## What @MethodSource is `@MethodSource` tells a `@ParameterizedTest` to obtain its argument sets from a **factory method** (a "provider method") rather than from inline literals. You pass the method's name as a string; JUnit calls it and feeds each returned element into one invocation. ```java @ParameterizedTest @MethodSource("squareCases") void squares(int base, int expected) { assertEquals(expected, base * base); } static Stream<Arguments> squareCases() { return Stream.of( Arguments.of(2, 4), Arguments.of(3, 9), Arguments.of(5, 25) ); } ``` ## What the factory may return The provider method must return something JUnit can iterate into argument sets: - `Stream<Arguments>` (most common), `Stream<Object[]>`, or a stream of single objects. - `Collection`, `Iterable`, `Iterator`, or an array of the same. - Primitive streams (`IntStream`, `LongStream`, `DoubleStream`) when there's a single parameter. **`Arguments`** is a JUnit interface representing one row of (possibly multiple) parameters; `Arguments.of(...)` (alias `arguments(...)`) builds one. For a **single-parameter** test you can return a stream of plain objects (e.g. `Stream<String>`) without wrapping in `Arguments`. ## Rules for the factory method 1. **Name resolution.** If you write `@MethodSource("squareCases")`, JUnit looks for `squareCases` in the **test class**. You can give a **fully-qualified** reference `@MethodSource("com.example.Cases#squareCases")` to point at a method in another class — useful for sharing data. An **empty** `@MethodSource` (no value) defaults to a factory with the **same name as the test method**. 2. **Static-ness.** The factory must be `static` **unless** the class is annotated `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`, in which case instance methods are allowed (because one test instance is reused for the whole class). 3. **No required parameters.** It normally takes no arguments. (It may accept parameters that JUnit's `ParameterResolver` can supply, but that's advanced.) 4. **Multiple methods.** You can list several: `@MethodSource({"smallCases", "largeCases"})` concatenates them. ## When @MethodSource is the right tool Reach for it when the data **can't be literals**: - **Real objects** — building a `User`, `BigDecimal`, `LocalDateTime`, or a configured fixture per case. - **Computed/generated** tables — e.g. random-but-seeded cases, a Cartesian product, values read from a service or file at runtime. - **Reuse** — the same dataset shared by several tests/classes via a fully-qualified reference. - **Complex expected results** — when the expected value is itself an object or collection. If your data *is* expressible as simple comma-separated literals, prefer the more declarative `@CsvSource`; if it's a single column of literals, `@ValueSource`. `@MethodSource` trades declarativeness for power. The next step up in flexibility is `@ArgumentsSource` with a reusable `ArgumentsProvider` class. ## Display names As with any parameterized test, the `name` template uses `{index}` and `{0}`, `{1}`… for the positional arguments, and `{argumentsWithNames}` when available. Returning `Arguments.of(...)` with meaningful values keeps the report readable; you can also use `Named.of("label", value)` to attach a display label to an otherwise opaque object. ## Common mistakes - Forgetting `static` without `PER_CLASS` → JUnit can't invoke the factory. - Misspelling the method name in the string (it's not compiler-checked) → resolution error at runtime. - Returning `List<Object[]>` but mis-sizing the arrays vs. the parameter list. - Returning a `Stream` and reusing it (streams are one-shot) — return a *fresh* stream each call.
- Must the @MethodSource factory be static?Yes, by default. The only exception is when the test class is annotated @TestInstance(Lifecycle.PER_CLASS), which reuses a single instance and therefore permits non-static factory methods.
- How do you reuse the same data set across multiple test classes?Use a fully-qualified reference: @MethodSource("com.example.TestData#commonCases"), pointing at a static factory in a shared class.
saying these in an interview costs you the question
- Returning a one-shot Stream and reusing it across calls
- Forgetting static (without PER_CLASS lifecycle)
- Assuming the method name string is compile-checked
- Using @MethodSource for data that is trivially expressible as @CsvSource literals