Testing Spring Boot Applications

🌿 Spring BootLesson 12Advanced

Spring Boot provides first-class testing support with @SpringBootTest, @WebMvcTest, @DataJpaTest, and Mockito. A solid test suite covers unit tests for services, slice tests for controllers, and integration tests with a real database.

1 Controller Slice Tests (@WebMvcTest)
Java — UserControllerTest.java
@WebMvcTest(UserController.class)  // Only loads web layer, not full context
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void getUser_whenExists_returns200() throws Exception {
        UserDto dto = new UserDto(1L, "Balaji", "b@test.com", Role.USER, true);
        when(userService.findById(1L)).thenReturn(dto);

        mockMvc.perform(get("/api/v1/users/1")
                .header("Authorization", "Bearer " + validToken))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Balaji"))
            .andExpect(jsonPath("$.email").value("b@test.com"));
    }

    @Test
    void createUser_withInvalidBody_returns400() throws Exception {
        var req = new CreateUserRequest("", "not-an-email", "weak");

        mockMvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(req)))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.code").value("VALIDATION_ERROR"))
            .andExpect(jsonPath("$.fieldErrors.name").exists())
            .andExpect(jsonPath("$.fieldErrors.email").exists());
    }
}
2 Repository Tests (@DataJpaTest)
Java — UserRepositoryTest.java
@DataJpaTest  // Uses in-memory H2, only loads JPA layer
@AutoConfigureTestDatabase(replace = Replace.NONE)  // Use real PostgreSQL
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TestEntityManager em;

    @Test
    void findByEmail_whenExists_returnsUser() {
        User user = new User("Balaji", "b@test.com", "hash", Role.USER);
        em.persistAndFlush(user);

        Optional<User> found = userRepository.findByEmail("b@test.com");

        assertThat(found).isPresent();
        assertThat(found.get().getName()).isEqualTo("Balaji");
    }

    @Test
    void existsByEmail_whenNotExists_returnsFalse() {
        assertThat(userRepository.existsByEmail("nobody@test.com")).isFalse();
    }
}
3 Code Challenge
Challenge: Write a full integration test using @SpringBootTest(webEnvironment = RANDOM_PORT) and TestRestTemplate that registers a user, logs in to get a JWT, then uses it to call a protected endpoint. Use Testcontainers to spin up a real PostgreSQL container.