How it works
The explanation, with examples you can run.
The web slice
@WebMvcTest(OrderController.class)class OrderControllerTest { @Autowired MockMvc mvc; @MockitoBean OrderService orders; // replaces the real bean in this context @Test void rejectsEmptyOrder() throws Exception { mvc.perform(post("/orders/7") .contentType(MediaType.APPLICATION_JSON) .content("{\"items\":[]}")) .andExpect(status().isBadRequest()); }}@WebMvcTest limits scanning to web components (controllers, @ControllerAdvice, filters, converters, interceptors, WebMvcConfigurers) and skips @Service, @Repository and ordinary @Configuration classes; collaborators are mocks. Spring Security's auto-configuration still applies, so @Import your own security configuration if the test should use its rules. MockMvc sends requests through the real DispatcherServlet without opening a socket. @MockitoBean comes from Spring Framework 6.2; the older @MockBean from Boot is deprecated since Boot 3.4.
The JPA slice
@DataJpaTest@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)@Testcontainersclass OrderRepositoryTest { @Container @ServiceConnection static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16"); @Autowired OrderRepository repo; @Test void findsRecentOrdersWithItems() { ... }}@DataJpaTest configures entities, repositories and a TestEntityManager; each test runs in a transaction that is rolled back. On its own it swaps in an embedded database if one is on the classpath. Replace.NONE keeps the real one, here a container that @ServiceConnection wires in automatically. Testing against the same database engine as production catches dialect differences, real constraint behaviour and the N+1 query counts an in-memory database would hide.
The whole application
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)@Testcontainersclass CheckoutFlowTest { @Container @ServiceConnection static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16"); @Autowired TestRestTemplate http; @Test void placesAnOrder() { var res = http.postForEntity("/orders/7", newOrder(), OrderView.class); assertThat(res.getStatusCode()).isEqualTo(HttpStatus.CREATED); }}Without webEnvironment, @SpringBootTest uses a mock web environment and starts no server. RANDOM_PORT starts the real embedded server, so filters, security, serialisation and transactions behave exactly as in production. One trap here: the test and the server run on different threads, so a @Transactional on the test method does not roll back what the server committed.
Keep the suite fast
Spring caches application contexts between test classes that use the same configuration. Every distinct combination of @MockitoBeans, properties or profiles creates a new context, so a suite with twenty slightly different set-ups starts twenty applications. Share a few base configurations, keep most tests as plain unit tests, and save @SpringBootTest for the flows that need everything.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.