What is the difference between MockMvcBuilders.standaloneSetup and webAppContextSetup, and when would you use each?
answer
- standalone = no context, you wire it, fastest/isolated
- webAppContext = real WebApplicationContext, full fidelity
- @WebMvcTest = sliced webAppContext (mock the services)
- standalone can silently drift from prod config
- standalone omits security filters unless added
basics
~20 sstandaloneSetup registers controllers manually with no Spring context — fast and focused but you wire everything yourself. webAppContextSetup builds MockMvc from a loaded WebApplicationContext, so real beans, filters, advice, and config apply — more realistic but heavier.
solid answer
~40 sMockMvcBuilders.standaloneSetup(controllers...) builds a MockMvc around just the controller instances you pass, with a minimal, hand-configured infrastructure — no Spring ApplicationContext is loaded. You inject mocked dependencies into the controller yourself and register only the pieces you want (custom argument resolvers, message converters, a specific @ControllerAdvice, interceptors). It's the fastest, most isolated option, ideal for a true unit test of one controller. MockMvcBuilders.webAppContextSetup(wac) builds MockMvc from a fully loaded WebApplicationContext, so every real bean, HandlerMapping, message converter, @ControllerAdvice, Filter, and Spring MVC config participates — the closest to production behavior. It's what @SpringBootTest + @AutoConfigureMockMvc and @WebMvcTest use under the hood. Trade-off: standalone = fast/isolated but you can silently diverge from real config; webAppContext = realistic but slower to start. In practice @WebMvcTest (sliced context) is the common middle ground.
code
java · 21 lines// standaloneSetup — pure unit test, no Spring context
MockMvc standalone = MockMvcBuilders
.standaloneSetup(new UserController(mockService))
.setControllerAdvice(new GlobalExceptionHandler())
.build();
// webAppContextSetup — full/real WebApplicationContext
@SpringBootTest
@AutoConfigureMockMvc
class FullIntegrationTest {
@Autowired MockMvc mockMvc; // built from the real WAC
}
// Manual webAppContextSetup variant:
@SpringJUnitWebConfig(AppConfig.class) // loads a WebApplicationContext
class ManualWacTest {
MockMvc mockMvc;
@BeforeEach void setup(@Autowired WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
}go deeper
Knows both build a MockMvc; standalone needs no context.
Uses @WebMvcTest routinely and knows it slices to the web layer.
Articulates fidelity-vs-speed trade-offs and the config-drift risk of standalone.
Sets team policy on which tier each test uses and where integration fidelity is required.
Both are static factory methods on **`MockMvcBuilders`** that produce a `MockMvc`, but they differ fundamentally in *how much of the real Spring MVC infrastructure participates*. **`MockMvcBuilders.standaloneSetup(Object... controllers)`** - Takes already-instantiated controller objects (you `new` them or build them with mocks injected). - **No `ApplicationContext` is loaded.** Spring builds a minimal, programmatically configured MVC environment behind the scenes. - You explicitly register anything you need through the returned `StandaloneMockMvcBuilder`: `.setControllerAdvice(...)`, `.setMessageConverters(...)`, `.setCustomArgumentResolvers(...)`, `.addInterceptors(...)`, `.addFilters(...)`, `.setValidator(...)`, `.setViewResolvers(...)`, `.defaultRequest(...)`. - **Pros:** fastest startup (no context scan), maximum isolation — a genuine unit test of the controller's mapping, binding, and logic. Dependencies are mocks you control. - **Cons:** it does **not** reflect your real application config. Global `@ControllerAdvice`, custom converters, security filters, Jackson customizations, formatters, etc. are absent unless you re-register them by hand — so a test can pass while production differs (or vice versa). Easy to drift. - **Use when:** you want a focused, fast unit test of one controller's request handling in isolation, and you're willing to wire the few infrastructure bits it needs. **`MockMvcBuilders.webAppContextSetup(WebApplicationContext context)`** - Requires a fully loaded `WebApplicationContext` — typically provided by the test framework via `@SpringBootTest` (or `@ContextConfiguration` + `@WebAppConfiguration`) with the context autowired in. - **The entire real MVC configuration participates:** all `HandlerMapping`s, `HandlerAdapter`s, registered `HttpMessageConverter`s, every `@ControllerAdvice`, `Filter`s (when added / auto-added), interceptors, argument resolvers, `Validator`s, and your `WebMvcConfigurer` customizations. - **Pros:** highest fidelity — closest to how requests behave in production. Catches integration issues standalone would miss (a misconfigured converter, an advice not applying, a filter ordering bug). - **Cons:** slower (a context must load), and broader — a failure could be anywhere in the wiring, not just the controller. - **Use when:** you want integration-level confidence that the controller works within the real Spring MVC setup. **Where the annotations fit:** - **`@WebMvcTest(SomeController.class)`** auto-configures a MockMvc via a *sliced* WebApplicationContext — only web-layer beans (controllers, `@ControllerAdvice`, converters, filters, Spring Security config) load; service/repository beans do **not**, so you supply them as `@MockitoBean`. This is effectively `webAppContextSetup` over a trimmed context — the pragmatic default for controller tests. - **`@SpringBootTest` + `@AutoConfigureMockMvc`** gives a MockMvc over the *full* application context (all beans real) — full integration, slowest. **Decision guide:** - Pure controller logic, fastest, isolated → `standaloneSetup`. - Realistic web slice with real advice/converters/security but mocked services → `@WebMvcTest`. - Full-stack in-process (real services, DB, etc.) → `@SpringBootTest` + `@AutoConfigureMockMvc`. **Gotchas:** - With `standaloneSetup`, Spring Security filters are absent unless you `.addFilters(springSecurityFilterChain)` — so authz isn't tested. `@WebMvcTest` wires Spring Security config, so you often need `.with(csrf())`/`@WithMockUser`. - Standalone can mask real problems: an exception mapped by a global `@ControllerAdvice` in production may become an unhandled 500 in a standalone test that didn't register the advice. - `webAppContextSetup(wac)` requires `@WebAppConfiguration` (or Boot's equivalent) so a `WebApplicationContext` (not a plain `ApplicationContext`) is created.
- Why can a standaloneSetup test pass while the endpoint misbehaves in production?Standalone loads no application context, so real @ControllerAdvice, custom message converters, formatters, security filters, and WebMvcConfigurer customizations are absent unless you re-register them. The test's infrastructure can diverge from production's.
- Does @WebMvcTest load your service and repository beans?No. It loads only the web slice (controllers, advice, converters, filters, security). You provide collaborators as @MockitoBean, keeping the test fast and focused on the web layer.
- How do you test Spring Security authorization with standaloneSetup?You must explicitly add the security filter chain via .addFilters(springSecurityFilterChain); otherwise security isn't applied. @WebMvcTest wires it automatically, which is why it's usually preferred for security-aware controller tests.
saying these in an interview costs you the question
- Claiming standaloneSetup exercises your real @ControllerAdvice and converters (it doesn't unless registered)
- Thinking @WebMvcTest loads the full application context / real services
- Believing standaloneSetup applies Spring Security filters automatically