@WebMvcTest vs @SpringBootTest for Controllers
Fast recall
Quick revision
Core answer
Whether the test boundary matches the MVC behavior or broader Boot wiring that the scenario needs
Assuming @WebMvcTest has one fixed bean list or @SpringBootTest automatically supplies a real database, server, and every integration
State the exact mappings, filters, services, persistence, server, and external systems that are real or replaced in each test
Interview-ready guide
Interview answer
@WebMvcTest creates a focused MVC slice and auto-configures MockMvc without a real server port. Ordinary services and repositories are outside its scanning, so I provide required collaborators with @MockitoBean, older @MockBean, or a small import. @SpringBootTest creates a broader Boot context, but the test's profiles, properties, mocks, exclusions, and webEnvironment still decide what starts. I choose @WebMvcTest for the MVC contract and @SpringBootTest when broader application wiring is part of the requirement. Neither has a fixed startup time, and @SpringBootTest does not automatically prove every database or external integration.
@WebMvcTest vs @SpringBootTest for Controllers
Direct Answer
@WebMvcTest creates a focused MVC test slice and auto-configures MockMvc without starting a server. @SpringBootTest creates a broader Boot ApplicationContext and can start a server for RANDOM_PORT or DEFINED_PORT. Neither has a fixed startup time, and neither automatically proves every external integration; choose the smallest boundary that includes the behavior under test.
Code Example
// MVC slice: service behavior is controlled by a test double.
@WebMvcTest(OrderController.class)
class OrderControllerSliceTest {
@Autowired MockMvc mockMvc;
@MockitoBean OrderService orderService;
@Test
void returnsOrder() throws Exception {
when(orderService.findById(1L)).thenReturn(new Order(1L, "Laptop", 999.0));
mockMvc.perform(get("/api/orders/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.item").value("Laptop"));
}
}
// Broader Boot context: only the boundaries configured for this test are real.
@SpringBootTest
@AutoConfigureMockMvc
class OrderControllerBootTest {
@Autowired MockMvc mockMvc;
@Test
void acceptsCreateRequest() throws Exception {
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"item\":\"Mouse\",\"price\":25.0}"))
.andExpect(status().isCreated());
}
}
The second test proves persistence only if its profile and configuration provide the intended database and the assertion checks stored state. @SpringBootTest alone does not make every boundary real or make the test end to end.
Common Mistakes & Pitfalls
- Choosing by habit: Use the smallest boundary that contains the behavior, not one annotation for every controller test.
- Leaving slice collaborators absent: Provide them with @MockitoBean, older @MockBean, @Import, or test configuration.
- Assuming a broad context means no mocks: Selective bean overrides can be useful when one remote dependency is outside the test scope.
- Calling a test end-to-end without real boundaries: State which server, database, broker, or external system is actually present.
Check your understanding
Answer these without looking back at the article.
- When would you choose @SpringBootTest with RANDOM_PORT over @WebMvcTest?
- Can you use MockMvc with @SpringBootTest?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.