skip to content

How does the @WebMvcTest slice actually decide which beans and auto-configurations to include, and how would you customize it?

level: principalimportance: nice to knowfreq 34%

answer

  1. WebMvcTypeExcludeFilter = which beans
  2. @OverrideAutoConfiguration(false) + @ImportAutoConfiguration = which infra
  3. AutoConfiguration.imports slice key
  4. @BootstrapWith WebMvcTestContextBootstrapper
  5. customize: controllers=/@Import/excludeAutoConfiguration/properties

basics

~10 s

@WebMvcTest is meta-annotated to restrict component scanning to web-layer stereotypes via a TypeExcludeFilter, and to enable only web-related auto-configurations listed in spring.factories/AutoConfiguration.imports. You customize it with controllers=, @Import, @AutoConfigure* annotations, or excludeAutoConfiguration.

solid answer

~40 s

Under the hood @WebMvcTest composes several things. @BootstrapWith(WebMvcTestContextBootstrapper) and a @TypeExcludeFilter (WebMvcTypeExcludeFilter) override normal component scanning so only web stereotypes — @Controller, @ControllerAdvice, filters, converters, WebMvcConfigurer — are picked up, and @Service/@Repository/@Component are filtered out. @OverrideAutoConfiguration(enabled=false) plus @ImportAutoConfiguration turns off Boot's usual auto-config and re-enables only the entries registered for the slice (the web MVC auto-configurations), so no DataSource/JPA is configured. It also applies @AutoConfigureMockMvc and @AutoConfigureCache/Json. You customize by: controllers=/value to scope which controllers load; @Import to add specific beans/config; property overrides via properties=; excludeAutoConfiguration= or excludeFilters to drop things; and stacking @AutoConfigure* annotations (e.g. @AutoConfigureMockMvc(print=...)). Any bean the loaded controllers need but the filter excludes must be provided via @MockitoBean or @Import.

code

java · 12 lines
java
// Scope + customize a slice explicitly
@WebMvcTest(
    controllers = ReportController.class,
    excludeAutoConfiguration = SecurityAutoConfiguration.class, // drop security
    properties = "spring.mvc.problemdetails.enabled=true"
)
@Import(JacksonCustomizationConfig.class) // add a specific web bean
class ReportControllerTest {
    @Autowired MockMvc mockMvc;
    @MockitoBean ReportService reportService;
    // ...
}

go deeper

for a junior

Not expected to know the internals.

for a middle

Can name a couple of customization levers (controllers=, @Import).

for a senior

Understands the exclude-filter vs auto-config-override split and common customizations.

for a principal

Reasons about the full meta-annotation stack, context caching implications, and when a slice's customization signals a need for @SpringBootTest.

## The composition of the annotation `@WebMvcTest` is itself a **meta-annotation** stack. Reading its definition reveals the machinery every slice shares: - **`@BootstrapWith(WebMvcTestContextBootstrapper)`** — plugs a custom `TestContextBootstrapper` into Spring's TestContext framework so the slice's context is built with web-test defaults. - **`@TypeExcludeFilters(WebMvcTypeExcludeFilter.class)`** — this is the heart of 'which beans'. During component scanning, this `TypeExcludeFilter` **excludes** components that are not part of the web layer. It keeps `@Controller`, `@ControllerAdvice`, `Filter`, `HandlerInterceptor`, `WebMvcConfigurer`, `Converter`/`Formatter`, `HandlerMethodArgumentResolver`, and Jackson `Module`s; it **drops** `@Component`, `@Service`, `@Repository`, and `@Configuration` that isn't web-related. When you pass `controllers=`, the filter further restricts to those specific controller classes. - **`@OverrideAutoConfiguration(enabled = false)`** — disables Boot's normal 'import everything on the classpath' auto-configuration. - **`@ImportAutoConfiguration`** — re-imports only the auto-configurations **registered for this slice**. Historically these were listed under a slice-specific key in `META-INF/spring.factories`; in current Boot they live in `META-INF/spring/…AutoConfiguration.imports` files keyed to the slice. This is why `DispatcherServletAutoConfiguration`, `WebMvcAutoConfiguration`, `JacksonAutoConfiguration`, error-handling and (if present) security auto-config are on, but `DataSourceAutoConfiguration`/`HibernateJpaAutoConfiguration` are off. - **`@AutoConfigureMockMvc`, `@AutoConfigureCache`, `@AutoConfigureWebMvc`, `@AutoConfigureJson`** — layered `@AutoConfigure*` annotations that switch on MockMvc, a no-op cache, MVC and JSON support. ## How this yields the observable behavior The combination of the type-exclude filter (bean scanning) and the override/import-auto-configuration pair (infrastructure) is precisely why the slice 'has controllers + MockMvc but no services or DB'. They are two independent mechanisms: one governs **your** components, the other governs **Boot's** auto-configured infrastructure. ## Customization levers - **Scope controllers**: `@WebMvcTest(controllers = {A.class, B.class})` or `value =`. - **Add beans/config**: `@Import(MyWebConfig.class)` or a nested `@TestConfiguration` to bring in a specific `@Bean`, an advice, or a real `SecurityFilterChain`. - **Provide missing collaborators**: `@MockitoBean` / `@MockitoSpyBean`. - **Drop auto-config**: `@WebMvcTest(excludeAutoConfiguration = SecurityAutoConfiguration.class)` to turn a piece off. - **Filter extra components**: `@WebMvcTest(..., excludeFilters = @Filter(...))` / `includeFilters`. - **Properties**: `@WebMvcTest(properties = "spring.mvc....=...")` or `@TestPropertySource`. - **Stack more @AutoConfigure**: e.g. `@AutoConfigureMockMvc(printOnlyOnFailure = true)`. ## Consequences for architecture / large suites - **Context caching**: the TestContext framework caches by the *effective* configuration (annotations, imports, mock sets, properties). Each unique slice config = a separate cached context. Standardizing slice setup (shared base test class, consistent `controllers=` and mock sets) maximizes cache hits and keeps the suite fast. - **Global `@Configuration` picked up unexpectedly**: any `@Configuration` matching the web filter (e.g. a `WebMvcConfigurer`) *will* load, which can drag in dependencies; be deliberate about what web config exists at the root package. - **Package placement**: slices scan from the test's package upward to find the `@SpringBootConfiguration`; misplaced tests can fail to find the main config. ## When to reach past the slice If customizing a slice starts to require importing much of the app, that's a signal the test wants `@SpringBootTest` instead — you're recreating the full context piecemeal.

  • Mechanistically, why are @Service beans absent but MockMvc present?
    Two separate mechanisms: WebMvcTypeExcludeFilter removes non-web stereotypes (like @Service) during component scanning, while @OverrideAutoConfiguration(false)+@ImportAutoConfiguration limit Boot's auto-config to the web slice's registered entries, which include MockMvc auto-config but exclude DataSource/JPA.
  • How does slice choice interact with TestContext caching in a big suite?
    Spring caches contexts by effective configuration; each distinct slice config (different controllers=, imports, mocks, properties) yields a new cached context. Many unique configs reduce cache reuse and slow the suite, so teams standardize slice setups via shared base classes.

saying these in an interview costs you the question

  • Thinking the slice just 'guesses' which beans to load with no mechanism
  • Believing you can't customize which auto-configs a slice enables
  • Assuming @Import brings back full auto-configuration
  • Claiming services are excluded by disabling auto-config (it's the type-exclude filter that removes stereotypes)

context