Spring Boot Testing

Testing verifies that your code works correctly. Spring Boot includes a full testing setup — JUnit 5, Mockito, and MockMvc — with no extra configuration. Write tests early, and they protect you from breaking things later.

Testing Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

This single starter includes JUnit 5, Mockito, AssertJ, MockMvc, and more.

Types of Tests in Spring Boot

Test Type            What Gets Loaded          Speed     Use For
─────────────────    ─────────────────────     ───────   ──────────────────────────
Unit test            Nothing (just the class)  Fastest   Service, utility logic
@WebMvcTest          Controller + MVC layer    Fast      REST endpoint behavior
@DataJpaTest         JPA + in-memory DB        Medium    Repository queries
@SpringBootTest      Full application          Slowest   Integration, end-to-end

Unit Testing a Service with Mockito

A unit test checks one class in isolation. Mock its dependencies so you test only the logic in that class:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;        ← Fake repository (no DB)

    @InjectMocks
    private UserService userService;              ← Real service, fake dependencies

    @Test
    void findById_returnsUser_whenUserExists() {
        // Arrange
        User user = new User(1L, "Alice", "alice@test.com");
        when(userRepository.findById(1L)).thenReturn(Optional.of(user));

        // Act
        User result = userService.findById(1L);

        // Assert
        assertThat(result.getName()).isEqualTo("Alice");
        verify(userRepository, times(1)).findById(1L);
    }

    @Test
    void findById_throwsException_whenUserNotFound() {
        when(userRepository.findById(99L)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> userService.findById(99L))
            .isInstanceOf(UserNotFoundException.class)
            .hasMessageContaining("99");
    }
}

Testing a Controller with @WebMvcTest

@WebMvcTest loads only the web layer — controllers, filters, and MVC config. No database or service beans load. Mock the service layer:

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;                        ← Simulates HTTP requests

    @MockBean
    private UserService userService;               ← Mock the service

    @Test
    void getUser_returns200_whenUserExists() throws Exception {
        User user = new User(1L, "Alice", "alice@test.com");
        when(userService.findById(1L)).thenReturn(user);

        mockMvc.perform(get("/api/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Alice"))
            .andExpect(jsonPath("$.email").value("alice@test.com"));
    }

    @Test
    void createUser_returns201_withValidBody() throws Exception {
        String body = """
            {"name": "Bob", "email": "bob@test.com", "age": 25}
        """;
        User saved = new User(2L, "Bob", "bob@test.com");
        when(userService.create(any())).thenReturn(saved);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(body))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").value(2));
    }
}

Testing the Repository with @DataJpaTest

@DataJpaTest                         ← Uses H2 in-memory DB, loads only JPA layer
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void findByEmail_returnsUser_whenEmailExists() {
        User user = new User(null, "Alice", "alice@test.com");
        userRepository.save(user);

        Optional<User> result = userRepository.findByEmail("alice@test.com");

        assertThat(result).isPresent();
        assertThat(result.get().getName()).isEqualTo("Alice");
    }
}

The Three A's of a Good Test

  Arrange   ← Set up test data and mocks
  Act       ← Call the method being tested
  Assert    ← Verify the result

@Test
void save_persistsUser() {
    // Arrange
    User user = new User(null, "Alice", "alice@test.com");

    // Act
    User saved = userService.save(user);

    // Assert
    assertThat(saved.getId()).isNotNull();
    assertThat(saved.getName()).isEqualTo("Alice");
}

Common Assertions (AssertJ)

assertThat(result).isEqualTo(expected);
assertThat(list).hasSize(3);
assertThat(string).contains("hello");
assertThat(number).isGreaterThan(0);
assertThat(optional).isPresent();
assertThat(value).isNull();
assertThatThrownBy(() -> method()).isInstanceOf(SomeException.class);

Summary

  • spring-boot-starter-test includes JUnit 5, Mockito, AssertJ, and MockMvc
  • Unit tests use @Mock and @InjectMocks to test a class in isolation
  • @WebMvcTest tests controller endpoints without loading the full app
  • @DataJpaTest tests repository methods using an in-memory database
  • Structure every test with Arrange, Act, and Assert sections

Leave a Comment

Your email address will not be published. Required fields are marked *