Testing Spring Boot Apps Using Testcontainers

Introduction

Testing real application behavior is difficult when relying on mocks or in-memory databases. Although unit tests are useful, they don’t reflect real-world scenarios. Testcontainers solves this by running real databases and services inside lightweight Docker containers during your tests. As a result, your Spring Boot integration tests become more accurate, stable, and production-like. In this post, you’ll learn how Testcontainers works, how to integrate it with Spring Boot, and how to write a complete integration test using real infrastructure components.

What Is Testcontainers?

Testcontainers is a Java library that provides throwaway Docker containers for testing. Instead of using mocks or embedded databases, your tests can spin up real services such as PostgreSQL, Redis, Kafka, or even complete microservices.

Key Benefits

  • Runs tests against real dependencies
  • Ensures stable, repeatable integration tests
  • Cleans up containers automatically
  • Works with JUnit 4 and JUnit 5
  • Perfect for microservices and CI pipelines

Because Testcontainers uses Docker, tests run the same way on every machine, reducing environment-related bugs.

Adding Testcontainers to Spring Boot

To get started, add the required dependencies to your pom.xml:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

You can replace PostgreSQL with MySQL, MongoDB, or any module you need.

Writing Your First Testcontainers Test

Here’s an example integration test using PostgreSQL and JUnit 5:

@Testcontainers
@SpringBootTest
public class UserRepositoryTest {

    @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 UserRepository userRepository;

    @Test
    void testCreateUser() {
        User user = new User(null, "Alice");
        userRepository.save(user);

        assertTrue(userRepository.findAll().size() > 0);
    }
}

This approach gives your test a real PostgreSQL instance running in Docker.

Using Testcontainers with Other Technologies

Testcontainers supports many common dependencies used in Spring microservices:

  • RedisContainer for caching
  • KafkaContainer for messaging
  • RabbitMQContainer for queues
  • MongoDBContainer for NoSQL tests
  • LocalStackContainer for AWS-compatible services

Therefore, you can test your entire system without running anything manually.

Example: Redis Test

@Container
static GenericContainer<?> redis =
    new GenericContainer<>("redis:7").withExposedPorts(6379);

Example: Kafka Test

@Container
static KafkaContainer kafka = new KafkaContainer();

Testcontainers automatically handles lifecycle, cleanup, and port mapping.

Why Testcontainers Improves Test Quality

Because Testcontainers runs actual services, tests are:

  • Closer to production behavior
  • More reliable across environments
  • Less dependent on mocks or hand-written fakes
  • Fully isolated, since each test gets its own environment

Consequently, developers catch bugs earlier and avoid “it works on my machine” problems.

Running Testcontainers in CI/CD

Testcontainers works in GitHub Actions, GitLab CI, Jenkins, and other CI systems. Most CI providers already support Docker, so containers run with no extra setup.

Best Practices for CI

  • Use lightweight images for faster builds
  • Cache Docker layers if possible
  • Run tests in parallel when supported
  • Stop unused containers to save resources

As long as Docker is available, Testcontainers behaves exactly the same as in local development.

Best Practices for Using Testcontainers

  • Prefer module-specific containers (PostgreSQLContainer, KafkaContainer, etc.)
  • Share containers across test classes where appropriate
  • Keep tests small and focused
  • Log container output when debugging
  • Use DynamicPropertySource to inject runtime values cleanly

These habits keep your tests fast, stable, and easy to maintain.

Final Thoughts

Testcontainers brings realism to your Spring Boot testing strategy by running real services inside disposable Docker containers. It eliminates mock-heavy tests, simplifies setup, and ensures your integration tests behave consistently across environments. Start with a simple database test, then expand into caching, messaging, or cloud-related tests as your project grows. For more backend testing techniques, read Service Discovery with Spring Cloud Eureka. For official documentation, visit the Testcontainers website.

Leave a Comment

Scroll to Top