Introduction: Why You Need This Spring Boot Testing with Testcontainers Tutorial

For years, the standard for integration testing in the Java ecosystem was to use an in-memory database like H2. It was fast, easy to set up, and required zero external dependencies. However, I’ve lost count of how many times my tests passed locally with H2, only to fail in production because of subtle SQL syntax differences or missing PostgreSQL-specific features like JSONB or GIN indexes. In this spring boot testing with testcontainers tutorial, I’m going to show you how to move past these limitations by using real Docker containers for your tests.

In my experience, ensuring environment parity is the most effective way to catch bugs early. If your production environment uses a specific version of PostgreSQL or Redis, your tests should too. Testcontainers makes this effortless by managing the lifecycle of Docker containers directly from your JUnit tests. This aligns perfectly with a solid spring boot dockerization guide strategy, where containerization isn’t just for deployment, but an integral part of the development lifecycle.

Prerequisites

Before we dive into the code, ensure you have the following installed on your machine:

Step 1: Adding the Necessary Dependencies

The first step in our spring boot testing with testcontainers tutorial is to update your pom.xml or build.gradle. Since Spring Boot 3.1, integration has become significantly easier with the introduction of the Testcontainers BOM (Bill of Materials).

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

By using the official Spring Boot starter for Testcontainers, we gain access to convenient annotations that handle the plumbing of connecting our application to the containerized database.

Step 2: Configuring the Test Container

Historically, we had to use @DynamicPropertySource to map the container’s random port to Spring’s spring.datasource.url. However, in this modern spring boot testing with testcontainers tutorial approach, we will use the @ServiceConnection annotation. This feature automatically configures the connection details for us.

@TestConfiguration(proxyBeanMethods = false)
public class TestcontainersConfiguration {

    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>("postgres:16-alpine");
    }
}

This configuration ensures that whenever our tests run, a fresh PostgreSQL 16 container is spun up, and Spring Boot is told exactly how to talk to it. If you run into issues with the container not starting, I recommend checking your spring boot logging best practices to enable DEBUG level logs for the org.testcontainers package.

Diagram showing the lifecycle of Testcontainers in a Spring Boot application
Diagram showing the lifecycle of Testcontainers in a Spring Boot application

Step 3: Writing the Integration Test

Now, let’s see it in action. We’ll write a repository test that saves a user and retrieves them. Unlike unit tests, this integration test will interact with a real, live database inside a Docker container.

@SpringBootTest
@Import(TestcontainersConfiguration.class)
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldSaveAndRetrieveUser() {
        User user = new User(null, "ajmani_dev", "contact@ajmani.dev");
        userRepository.save(user);

        Optional<User> found = userRepository.findByUsername("ajmani_dev");
        assertThat(found).isPresent();
        assertThat(found.get().getEmail()).isEqualTo("contact@ajmani.dev");
    }
}

When you run this test, you’ll notice in your Docker dashboard that a container starts up, the test runs, and then the container is automatically destroyed. This ephemeral nature is why I emphasize following a spring boot dockerization guide for your entire team—it keeps local environments clean.

Terminal window showing the successful startup of a Testcontainers PostgreSQL instance
Terminal window showing the successful startup of a Testcontainers PostgreSQL instance

Pro Tips for Spring Boot Testing with Testcontainers

Once you get the basics down, you might notice that starting a container for every test class is slow. Here are some advanced tips to optimize your spring boot testing with testcontainers tutorial implementation:

For better visibility into what’s happening during these startup phases, revisit your spring boot logging best practices to ensure you aren’t swallowing important container errors.

Troubleshooting Common Issues

Even with a perfect spring boot testing with testcontainers tutorial, you might hit some snags. Here are the most common issues I encounter:

What’s Next?

Now that you’ve mastered the basics of this spring boot testing with testcontainers tutorial, consider expanding your test suite to include other technologies. You can use Testcontainers for Redis, Kafka, Elasticsearch, or even LocalStack for AWS services. The principle remains the same: use real infrastructure to gain real confidence.

If you’re looking to dive deeper into container optimization, check out our guide on spring boot dockerization to see how to minify your production images once your tests have passed.