skip to content

What is the difference between MockMvcBuilders.standaloneSetup and webAppContextSetup, and when would you use each?

level: seniorimportance: must knowfreq 60%

answer

  1. standalone = no context, you wire it, fastest/isolated
  2. webAppContext = real WebApplicationContext, full fidelity
  3. @WebMvcTest = sliced webAppContext (mock the services)
  4. standalone can silently drift from prod config
  5. standalone omits security filters unless added

basics

~20 s

standaloneSetup 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 s

MockMvcBuilders.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
java
// 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

for a junior

Knows both build a MockMvc; standalone needs no context.

for a middle

Uses @WebMvcTest routinely and knows it slices to the web layer.

for a senior

Articulates fidelity-vs-speed trade-offs and the config-drift risk of standalone.

for a principal

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

context