Integration Testing

Write Spring Boot integration tests with @SpringBootTest, Testcontainers, and @DataJpaTest — testing real component interactions with actual databases.

Last reviewed: January 2025

·Spring Boot 2.7Spring Boot 3.xTestcontainers 1.19
✅
Quick Understanding: @SpringBootTest starts the full Spring ApplicationContext for realistic integration tests. Use @DataJpaTest for repository-only tests with an in-memory database. Use Testcontainers to run real PostgreSQL/MySQL in Docker during tests. Integration tests are slower than unit tests but test real interactions between layers.

🔍 What Is It?

Integration tests verify that multiple components work together correctly.

Spring Boot test slices:

  • →@SpringBootTest — full application context (all beans)
  • →@WebMvcTest — web layer only (controllers, filters)
  • →@DataJpaTest — JPA layer only (repositories, entities)
  • →@DataRedisTest — Redis layer only
  • →@RestClientTest — REST client testing

Test support:

  • →@MockBean — replace a bean with a Mockito mock in the context
  • →@SpyBean — wrap a real bean with a Mockito spy
  • →@Sql — execute SQL before/after tests
  • →@Transactional on test class — rolls back after each test
  • →Testcontainers — real databases in Docker

🎯 Why Do We Need It?

Unit tests can't catch integration bugs:

  • →Wrong SQL query in @Query annotation
  • →Transaction propagation issues
  • →Security configuration blocking valid requests
  • →Entity mapping problems
  • →Auto-configuration conflicts

Integration tests catch these by running real components together.

🏗️ What Happens in Real Projects?

Integration test strategy:

  • →Repository tests with @DataJpaTest + H2 or Testcontainers
  • →Controller tests with @WebMvcTest + @MockBean for services
  • →Full flow tests with @SpringBootTest for critical paths
  • →Security tests verifying authentication and authorization

⚙️ How It Works

@SpringBootTest creates an ApplicationContext with all beans. @DataJpaTest creates a minimal context with only JPA-related beans and an embedded database. Test slices are much faster than full @SpringBootTest because they load fewer beans.

💻 Example

Integration Testing Patterns — @DataJpaTest, @WebMvcTest, and Testcontainers.

java
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryTest {

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

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

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private TestEntityManager entityManager;

    @Test
    void findByCustomerIdAndStatus_returnsMatchingOrders() {
        // Given — persist test data
        Order order1 = entityManager.persist(Order.builder()
            .customerId(1L)
            .status(OrderStatus.PENDING)
            .orderNumber("ORD-001")
            .totalAmount(BigDecimal.TEN)
            .build());
        entityManager.persist(Order.builder()
            .customerId(1L)
            .status(OrderStatus.DELIVERED)
            .orderNumber("ORD-002")
            .totalAmount(BigDecimal.ONE)
            .build());
        entityManager.flush();

        // When
        List<Order> result = orderRepository
            .findByCustomerIdAndStatus(1L, OrderStatus.PENDING);

        // Then
        assertThat(result).hasSize(1);
        assertThat(result.get(0).getOrderNumber()).isEqualTo("ORD-001");
    }
}

🤔 Common Confusions

❓ When should I use @SpringBootTest vs @WebMvcTest?

@WebMvcTest is faster — it only loads the web layer (controllers, filters, security). Use it for controller tests. @SpringBootTest loads everything — use it for full integration tests that need the complete application context (e.g., testing a full request flow through controller → service → repository).

❓ Should I use H2 or Testcontainers for database tests?

Testcontainers with the real database (PostgreSQL, MySQL). H2 is a different database — it doesn't support all PostgreSQL features (JSONB, window functions, specific constraints). Tests that pass with H2 can fail with real PostgreSQL. Testcontainers adds startup time but gives you confidence that tests reflect production behavior.

🚨 Common Production Issues

@SpringBootTest used for all tests — makes the test suite take minutes instead of seconds

Tests sharing state via @Autowired repositories without @Transactional rollback

Testcontainers starting a new container per test class — use static containers for reuse

✅ Best Practices

Use test slices (@WebMvcTest, @DataJpaTest) instead of @SpringBootTest when possible

Use Testcontainers for repository tests — test against the real database

Add @Transactional to test classes to auto-rollback after each test

Use @Sql to set up test data declaratively

Share Testcontainers instances across test classes with static containers

🏛️ Architect's Note

Senior Architect's Perspective

The test suite performance matters. A suite that takes 30 minutes to run means developers avoid running it. Target: unit tests < 30 seconds, integration tests < 5 minutes. Use test slices aggressively. Share Testcontainers instances with the Singleton pattern (static container + @DynamicPropertySource). Spring Boot 3.2+ supports Testcontainers auto-configuration — define containers in application.properties.

💬 Frequently Asked Questions

How do I speed up @SpringBootTest?

Use @SpringBootTest only when necessary. Use context caching — Spring caches the ApplicationContext between tests with the same configuration. Don't use @DirtiesContext unless necessary — it forces context recreation. Use @MockBean sparingly — each unique @MockBean combination creates a new context.

🎯 Key Takeaways

1@SpringBootTest loads full context; test slices load only what's needed
2@WebMvcTest for controller tests; @DataJpaTest for repository tests
3Testcontainers provides real database instances for integration tests
4@MockBean replaces a bean in the Spring context with a Mockito mock
5@Transactional on test class auto-rolls back after each test
6Prefer test slices over @SpringBootTest for faster test execution

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Spring Boot references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Spring Boot learning path

Browse the full Spring Boot topic collection and learning order on SplashRide.

Spring Boot Interview Prep

Practice Spring Boot interview questions and scenario-based answers.

Backend Engineer Career Path

A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Unit Testing

Write effective unit tests for Spring Boot services using JUnit 5, Mockito, and AssertJ — fast, isolated tests that don't start a Spring context.

MockMvc

Test Spring MVC controllers with MockMvc — the fluent API for testing HTTP request handling, response status, JSON content, and security without starting a real server.