16.4 Context Slicing: @WebMvcTest and @DataJpaTest
@SpringBootTest loads the full application context and is very slow. Slice Tests selectively load only the beans needed for a specific layer, dramatically improving test speed.
1. @WebMvcTest — Load Controllers Only
Validates Controller-layer HTTP request/response logic without services or repositories.
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean // Registers a Mock bean in the Spring context (different from @Mock!)
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("POST /api/users — valid DTO returns 201 Created")
void create_user_returns_201() throws Exception {
CreateUserRequest request = new CreateUserRequest("John Doe", "john@test.com");
UserDto createdUser = new UserDto(1L, "John Doe", "john@test.com");
given(userService.createUser(any())).willReturn(createdUser);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.name").value("John Doe"))
.andDo(print());
}
}
2. @DataJpaTest — Load JPA Beans Only
Validates repository query logic with only JPA repositories and EntityManager loaded. Uses in-memory H2 by default.
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE) // Use real DB (Testcontainers) instead of H2
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Autowired
private TestEntityManager entityManager;
@Test
@DisplayName("User can be found by email.")
void find_user_by_email() {
entityManager.persist(new User("Alice", "alice@test.com"));
entityManager.flush();
entityManager.clear(); // Clear 1st-level cache to force real DB query
Optional<User> result = userRepository.findByEmail("alice@test.com");
assertThat(result).isPresent();
assertThat(result.get().getName()).isEqualTo("Alice");
}
}