Testing Spring Boot Applications with JUnit 5 and Mockito


Published: November 3, 2024


Testing is a cornerstone of professional software development, ensuring your Spring Boot application works correctly, stays maintainable, and evolves confidently. In this post, we’ll explore how to leverage JUnit 5 and Mockito for effective unit and integration testing of Spring Boot applications.


Why JUnit 5 and Mockito?

  • JUnit 5 is the modern testing framework with modular architecture and improved features over JUnit 4.
  • Mockito is the leading mocking framework to simulate dependencies and isolate units for testing.
  • Spring Boot’s test starter integrates well with both, making testing straightforward.

Getting Started: Setup Dependencies

In your pom.xml for Maven or build.gradle for Gradle, add:

Maven:

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

This includes JUnit 5, Mockito, and Spring Test libraries.


Writing Unit Tests with JUnit 5 and Mockito

Example 1: Testing a Service with Mocked Repository

Assume a simple service layer:

@Service
public class UserService {

  private final UserRepository userRepository;

  public UserService(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public User findUserById(Long id) {
    return userRepository.findById(id).orElse(null);
  }
}

Create a test class:

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

  @Mock
  private UserRepository userRepository;

  @InjectMocks
  private UserService userService;

  @Test
  void testFindUserById_UserExists() {
    User user = new User(1L, "Alice");
    Mockito.when(userRepository.findById(1L)).thenReturn(Optional.of(user));

    User result = userService.findUserById(1L);

    Assertions.assertNotNull(result);
    Assertions.assertEquals("Alice", result.getName());
  }

  @Test
  void testFindUserById_UserNotFound() {
    Mockito.when(userRepository.findById(2L)).thenReturn(Optional.empty());

    User result = userService.findUserById(2L);

    Assertions.assertNull(result);
  }
}

Explanation:

  • @Mock creates a mock of the repository.
  • @InjectMocks injects the mock into the service.
  • Mockito’s when(...).thenReturn(...) configures mock behavior.
  • Assertions verify outcomes.

Integration Testing with Spring Boot Test

Sometimes, you want to test components together with the Spring context.

Example 2: Testing Controller with Spring Boot

@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {

  @Autowired
  private MockMvc mockMvc;

  @Test
  void testGetUser() throws Exception {
    mockMvc.perform(get("/users/1"))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$.name").value("Alice"));
  }
}

Here:

  • @SpringBootTest boots the entire Spring context.
  • MockMvc performs HTTP request simulations without a real server.
  • jsonPath verifies JSON response structure.

Mocking Beans in Spring Context

If you want to mock certain beans in an integration test, use:

@MockBean
private UserRepository userRepository;

This will replace the real bean with a mock in the Spring context.


Advanced Tips

  • Use parameterized tests (@ParameterizedTest) in JUnit 5 to run the same test with different inputs.
  • Leverage ArgumentCaptor in Mockito to verify method arguments.
  • Use @Nested tests in JUnit 5 to organize test cases.
  • Consider Testcontainers for integration tests with real databases or message brokers.

Summary

JUnit 5 and Mockito provide a powerful combo to test Spring Boot applications, from isolated units to full integration tests. Proper use of mocks, assertions, and Spring testing support ensures robust, maintainable tests.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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