DEV Community

Cover image for Testing Spring Boot Applications: An Introduction to Unit and Integration Testing
Igor Venturelli
Igor Venturelli

Posted on • Originally published at igventurelli.io

Testing Spring Boot Applications: An Introduction to Unit and Integration Testing

Learn unit and integration testing for Spring Boot apps with JUnit, Mockito, and Spring Boot's testing tools in this beginner-friendly guide

Testing is a critical part of modern software development, ensuring that your application works as expected while minimizing bugs. In Spring Boot, testing is both powerful and straightforward, thanks to robust support for unit and integration testing. Whether you're verifying business logic with unit tests or ensuring the interplay of components with integration tests, understanding the fundamentals is essential for building reliable applications.

The Fundamentals of Testing in Spring Boot

Spring Boot’s testing ecosystem integrates seamlessly with JUnit, the de facto standard for Java testing. Additionally, Spring’s built-in testing utilities simplify configuration management, dependency injection, and context loading, making it easier to write meaningful tests.

Unit testing focuses on isolated components, typically a single class, without involving external dependencies like databases or APIs. On the other hand, integration testing evaluates how multiple components work together, often requiring a more extensive setup.

Writing Unit Tests with JUnit and Mockito

Unit tests in Spring Boot typically involve using JUnit and Mockito to verify the behavior of a single component. Dependency injection helps mock collaborators, ensuring that each test isolates the logic of the class under test.

Example: Testing a Service Layer

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class UserServiceTest {

    @InjectMocks
    private UserService userService;

    @Mock
    private UserRepository userRepository;

    @Test
    void testGetUserById() {
        MockitoAnnotations.openMocks(this);
        User mockUser = new User(1L, "John Doe", "john.doe@example.com");
        when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));

        User result = userService.getUserById(1L);

        assertNotNull(result);
        assertEquals("John Doe", result.getName());
        verify(userRepository, times(1)).findById(1L);
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • Mockito is used to mock the UserRepository.
  • Assertions verify the behavior of the UserService.
  • The verify method ensures the correct repository method is invoked.

Writing Integration Tests with Spring Boot

Integration tests validate the interactions between multiple layers, such as the controller, service, and repository. Spring Boot’s @SpringBootTest annotation loads the entire application context, making it ideal for such tests.

Example: Testing a REST Controller

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void testGetUserById() {
        ResponseEntity<User> response = restTemplate.getForEntity("/api/users/1", User.class);

        assertThat(response.getStatusCodeValue()).isEqualTo(200);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().getName()).isEqualTo("John Doe");
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • The @SpringBootTest annotation loads the full application context.
  • The TestRestTemplate simulates an HTTP call to the REST endpoint.
  • Assertions verify the HTTP response and the returned data.

Managing Test Configurations

For integration tests, use a separate configuration to avoid polluting your main database or environment. Spring Boot supports in-memory databases like H2, which are ideal for testing.

Example: application-test.yml

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: create-drop
Enter fullscreen mode Exit fullscreen mode

Switch the profile to test for integration tests:

@SpringBootTest(properties = "spring.profiles.active=test")
Enter fullscreen mode Exit fullscreen mode

This ensures that your integration tests run in an isolated environment.

Mocking vs. Real Dependencies in Integration Tests

When writing integration tests, decide whether to use mock dependencies or real ones. For instance:

  • Use mock dependencies for external APIs to ensure consistent results.
  • Use real dependencies like an in-memory database for testing repositories.

Example: Mocking an External API

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
class ExternalApiServiceTest {

    @MockBean
    private ExternalApiClient externalApiClient;

    @Test
    void testFetchData() {
        when(externalApiClient.getData()).thenReturn("Mock Data");

        String result = externalApiService.fetchData();

        assertEquals("Mock Data", result);
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Testing Spring Boot applications is essential for building robust, maintainable systems. By combining unit tests with JUnit and integration tests with Spring’s testing support, you can cover your application comprehensively. Whether isolating logic with Mockito or verifying complex interactions with @SpringBootTest, these tools ensure your application works as intended.

Start incorporating these testing practices into your workflow, and you’ll not only catch bugs early but also build confidence in your codebase’s reliability.


Let’s connect!

📧 Don’t Miss a Post! Subscribe to my Newsletter!
➡️ LinkedIn
🚩 Original Post
☕ Buy me a Coffee

Top comments (0)