skip to content

How does method-level parameter injection work in Spring JUnit 5 tests, and what can you inject into @Test / @BeforeEach methods?

level: seniorimportance: should knowfreq 40%

answer

  1. ParameterResolver applies to @Test/@BeforeEach/@BeforeAll too
  2. Inject beans + ApplicationContext/WebApplicationContext
  3. Build MockMvc from injected WebApplicationContext
  4. Exactly one resolver per parameter — @Autowired breaks ties
  5. Resolved at invocation time

basics

~20 s

Via the same ParameterResolver, SpringExtension supplies parameters to test lifecycle methods. You can declare beans (optionally with @Autowired/@Qualifier/@Value) as parameters of @Test, @BeforeEach, @BeforeAll, etc., plus special types like ApplicationContext and WebApplicationContext, and Spring resolves them at invocation.

solid answer

~40 s

SpringExtension's ParameterResolver applies to any JUnit 5 executable — not just the constructor. So @Test, @RepeatedTest, @ParameterizedTest, @BeforeEach/@AfterEach, and @BeforeAll/@AfterAll methods can declare parameters that Spring resolves from the ApplicationContext. You inject beans directly (add @Autowired/@Qualifier/@Value on the parameter if a resolver conflict must be broken), and you can inject infrastructure types such as ApplicationContext or (with @SpringJUnitWebConfig) WebApplicationContext — commonly to build MockMvc in @BeforeEach. Spring coexists with JUnit-native resolvers (TestInfo, TestReporter) and third-party ones (e.g. @ParameterizedTest arguments) because JUnit routes each parameter to the one resolver that claims it; a clash is an error, which the @Autowired signal avoids. This keeps per-method setup explicit without field state.

code

java · 18 lines
java
@SpringJUnitWebConfig(WebConfig.class)
class UserControllerTest {

    MockMvc mockMvc;

    // WebApplicationContext injected into the lifecycle method
    @BeforeEach
    void setUp(WebApplicationContext wac) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    // Mix JUnit-native TestInfo with a Spring bean parameter
    @Test
    void listsUsers(TestInfo info, @Autowired UserService users) throws Exception {
        assertThat(users.count()).isGreaterThanOrEqualTo(0);
        mockMvc.perform(get("/users")).andExpect(status().isOk());
    }
}

go deeper

for a junior

Know that @Test/@BeforeEach methods can take bean parameters that Spring fills in.

for a middle

Explain injecting ApplicationContext/WebApplicationContext and using @Autowired on parameters.

for a senior

Articulate the single-resolver-per-parameter contract and how Spring coexists with TestInfo/parameterized/Mockito resolvers.

for a principal

Design test infrastructure (MockMvc setup, scoped collaborators) around resolver semantics and reason about ambiguity failure modes across combined extensions.

## One mechanism, many sites JUnit 5 resolves parameters for **every** user-supplied executable: the test constructor, `@Test` methods, and every lifecycle method (`@BeforeAll`, `@AfterAll`, `@BeforeEach`, `@AfterEach`), as well as `@RepeatedTest`/`@ParameterizedTest`/`@TestFactory`. Because `SpringExtension` is a `ParameterResolver`, it can supply Spring-managed values at any of these sites. ### Injecting beans into methods ```java @SpringJUnitConfig(AppConfig.class) class ReportTest { @Test void generates(ReportService service, @Qualifier("pdf") Renderer renderer) { assertThat(service.render(renderer)).isNotEmpty(); } } ``` Spring resolves `ReportService` and the qualified `Renderer` from the context per method invocation. ### Injecting infrastructure types Spring can also inject well-known container types, most usefully: - `ApplicationContext` / `WebApplicationContext` — e.g. to build `MockMvc`: ```java @BeforeEach void setUp(WebApplicationContext wac) { this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } ``` ### Coexistence with other resolvers JUnit 5 itself resolves `TestInfo`, `TestReporter`, and `RepetitionInfo`. `@ParameterizedTest` sources resolve the value parameters. Third-party extensions (`MockitoExtension` for `@Mock` params) resolve their own. JUnit's contract: **exactly one** resolver may claim a given parameter — two claimants is a `ParameterResolutionException`. Spring stays out of the way: it only claims a parameter when it's the sole capable resolver **or** when the parameter carries `@Autowired`, `@Qualifier`, or `@Value`. So you mix freely: ```java @Test void mixed(TestInfo info, @Autowired ClockService clock) { ... } ``` ## Disambiguation rules (the gotcha) - If both Spring and another extension can supply a parameter type, add `@Autowired`/`@Qualifier`/`@Value` so Spring claims it — otherwise you may hit an ambiguity error, or the wrong resolver wins. - Conversely, if you *don't* want Spring to try resolving a `String` (which it could via `@Value`), just don't annotate it; a bare `String` on a `@ParameterizedTest` goes to the argument source. ## Lifecycle notes - `@BeforeAll`/`@AfterAll` are static by default (or non-static with `@TestInstance(PER_CLASS)`); parameter injection works either way. - Parameters are resolved **at invocation time**, so each `@BeforeEach` call gets a freshly resolved parameter (same singleton bean instance from the cached context, unless prototype/scoped). ## When to use Method parameter injection shines for **per-method-scoped** collaborators, for grabbing the `WebApplicationContext`/`ApplicationContext` to build test infrastructure, and for keeping tests free of mutable field state. For dependencies used by *every* test, field or constructor injection is usually tidier.

  • Can you inject a WebApplicationContext into a test using plain @SpringJUnitConfig?
    No. @SpringJUnitConfig loads a plain ApplicationContext, not a WebApplicationContext. You'd get a resolution failure (the bean type isn't a WebApplicationContext). Use @SpringJUnitWebConfig (which adds @WebAppConfiguration) to get a WebApplicationContext to inject.
  • What happens if both SpringExtension and another extension can resolve the same @Test parameter and neither is disambiguated?
    JUnit 5 throws a ParameterResolutionException because more than one ParameterResolver claims the parameter. Adding @Autowired/@Qualifier/@Value makes Spring the explicit claimant (or you scope the other resolver's annotation), resolving the ambiguity.

saying these in an interview costs you the question

  • Thinking only fields/constructors can be injected, not method parameters
  • Believing you can inject WebApplicationContext without @WebAppConfiguration/@SpringJUnitWebConfig
  • Assuming Spring always wins parameter resolution regardless of other extensions

context