Integration Testing with Spring Boot


Table of Contents

  1. Introduction to Integration Testing
  2. Difference Between Unit and Integration Testing
  3. Setting Up Integration Tests in Spring Boot
  4. Using @SpringBootTest for Full-Context Testing
  5. Testing REST Controllers with TestRestTemplate and MockMvc
  6. Testing Repositories with Embedded Databases
  7. Using Testcontainers for Realistic Integration Tests
  8. Transaction Rollbacks and Test Isolation
  9. Data Initialization for Tests (@Sql, @DataJpaTest, @TestEntityManager)
  10. Best Practices for Integration Testing
  11. Summary

1. Introduction to Integration Testing

Integration testing ensures that different layers or components of your application work together correctly. It verifies the behavior of the application in a more realistic runtime environment, where various components interact.

In Spring Boot, integration tests are used to:

  • Test REST API endpoints from controller to database.
  • Validate Spring configurations and beans.
  • Simulate user behavior end-to-end.

2. Difference Between Unit and Integration Testing

FeatureUnit TestingIntegration Testing
ScopeTests single class or methodTests multiple components
SpeedVery fastSlower due to more setup
DependenciesTypically mockedUse real components
FrameworksJUnit + MockitoJUnit + Spring Boot Test, Testcontainers, etc.

3. Setting Up Integration Tests in Spring Boot

Dependencies in Maven

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

<!-- For Testcontainers (optional) -->
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.1</version>
    <scope>test</scope>
</dependency>

4. Using @SpringBootTest for Full-Context Testing

The @SpringBootTest annotation loads the full application context.

javaCopyEdit@SpringBootTest
class UserServiceIntegrationTest {
    @Autowired
    private UserService userService;

    @Test
    void testCreateUser() {
        User user = userService.create("Alice");
        assertNotNull(user.getId());
    }
}

This test will initialize all Spring components, configurations, and data sources.


5. Testing REST Controllers with TestRestTemplate and MockMvc

With TestRestTemplate

javaCopyEdit@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void testGetUser() {
        ResponseEntity<User> response = restTemplate.getForEntity("/users/1", User.class);
        assertEquals(HttpStatus.OK, response.getStatusCode());
    }
}

With MockMvc

javaCopyEdit@AutoConfigureMockMvc
@SpringBootTest
class UserControllerMockMvcTest {
    @Autowired
    private MockMvc mockMvc;

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

6. Testing Repositories with Embedded Databases

You can use @DataJpaTest to test repositories with an in-memory database (like H2):

javaCopyEdit@DataJpaTest
class UserRepositoryTest {
    @Autowired
    private UserRepository repository;

    @Test
    void testFindById() {
        User saved = repository.save(new User("Alice"));
        Optional<User> found = repository.findById(saved.getId());
        assertTrue(found.isPresent());
    }
}

7. Using Testcontainers for Realistic Integration Tests

Instead of using H2, Testcontainers allows you to run real database instances (like PostgreSQL) in Docker containers for more production-like tests.

javaCopyEdit@Testcontainers
@SpringBootTest
class PostgreSQLContainerTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
        .withDatabaseName("testdb")
        .withUsername("user")
        .withPassword("pass");

    @DynamicPropertySource
    static void setDatasourceProps(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Test
    void testDbInteraction() {
        // use the real PostgreSQL instance
    }
}

8. Transaction Rollbacks and Test Isolation

By default, Spring tests annotated with @Transactional will roll back transactions after each test:

javaCopyEdit@Transactional
@Test
void testTransactionalRollback() {
    userRepository.save(new User("Bob"));
    // data won't persist after the test
}

This keeps tests isolated and database state clean.


9. Data Initialization for Tests

You can preload data using:

@Sql Annotation

javaCopyEdit@Sql("/test-data.sql")
@Test
void testWithPreloadedData() {
    // test logic
}

@TestEntityManager (in @DataJpaTest)

javaCopyEdit@Autowired
private TestEntityManager entityManager;

@Test
void testUsingEntityManager() {
    User user = new User("Carol");
    entityManager.persist(user);
}

10. Best Practices for Integration Testing

  • Use @SpringBootTest only when needed. Prefer @WebMvcTest and @DataJpaTest for lighter tests.
  • Reset the database state between tests to prevent data leaks.
  • Use Testcontainers for a more realistic test environment.
  • Keep your test environment configuration (like test DB credentials) separate from production.
  • Avoid complex logic in tests—focus on expected behavior.

11. Summary

Integration testing in Spring Boot ensures your application components work together seamlessly. From REST endpoints to database operations, it validates the actual behavior of your application under realistic conditions. By combining tools like @SpringBootTest, MockMvc, TestRestTemplate, and Testcontainers, you can build a robust and maintainable testing suite for your Spring Boot application.