How does method-level parameter injection work in Spring JUnit 5 tests, and what can you inject into @Test / @BeforeEach methods?
answer
- ParameterResolver applies to @Test/@BeforeEach/@BeforeAll too
- Inject beans + ApplicationContext/WebApplicationContext
- Build MockMvc from injected WebApplicationContext
- Exactly one resolver per parameter — @Autowired breaks ties
- Resolved at invocation time
basics
~20 sVia 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 sSpringExtension'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@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
Know that @Test/@BeforeEach methods can take bean parameters that Spring fills in.
Explain injecting ApplicationContext/WebApplicationContext and using @Autowired on parameters.
Articulate the single-resolver-per-parameter contract and how Spring coexists with TestInfo/parameterized/Mockito resolvers.
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