CodeGym /Courses /Module 5. Spring /Lecture 269: Hands-on: setting up Testcontainers for test...

Lecture 269: Hands-on: setting up Testcontainers for testing with databases

Module 5. Spring
Level 21 , Lesson 8
Available

In previous lectures we dove into testing microservice applications. We went through different approaches and types of testing, including unit testing with JUnit and Mockito, integration testing of REST APIs using MockMvc, and we also covered contract testing with Pact. We started picking tools that help ensure reliable interactions between components and services. Now it's time to look at how to test interactions with databases using a Docker-based testing tool — Testcontainers.

Earlier we found that testing with databases can be tricky because you need data isolation and a realistic environment. Today we'll fix that!

Why use Testcontainers?

Imagine you're testing database interactions and someone accidentally inserts invalid data into the test DB. Your tests fail, you start debugging, and it's mess. Or another scenario: you're testing against PostgreSQL locally, but production runs on MySQL. What do you do? The test environment should be:

  1. Isolated — so every test run doesn't depend on external factors.
  2. Realistic — so tests run with the same settings as production.
  3. Easy to set up — so it doesn't require complex infra.

Testcontainers lets you run Docker containers during test execution, providing an isolated and realistic environment for testing. That means you can use the same databases, message brokers, and other dependencies that are used in the real world. Ready? Let's go!


Getting started with Testcontainers

  1. Let's add dependencies

    First, connect Testcontainers to your project. If you're using PostgreSQL (a tester favorite), your dependencies in pom.xml will look like this:

    
    <dependencies>
        <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>
    </dependencies>
    

    If you're using Gradle:

    
    testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
    testImplementation 'org.testcontainers:postgresql:1.19.0'
    

    Testcontainers supports many other databases like MySQL, MongoDB, Oracle, and even Kafka and RabbitMQ. You can find the full list in the official Testcontainers documentation.

  2. Make sure Docker is installed Testcontainers works through Docker, so make sure it's installed and running on your machine.
  3. Minimal Docker configuration Make sure Docker can run database containers. A quick check is to run the docker run command directly for PostgreSQL:
    docker run --name postgres-test -e POSTGRES_PASSWORD=test -d postgres:latest
    

    If the container starts without errors, you're good to go!


Setting up a test using Testcontainers

1. Starting a PostgreSQL container

Let's start by creating the simplest PostgreSQL container for tests. Here's an example:


import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class PostgresContainerTest {

    @Test
    void testPostgresContainer() {
        // Create PostgreSQL container
        try (PostgreSQLContainer
    postgresContainer = new PostgreSQLContainer<>("postgres:latest")) {
            postgresContainer.start();

            // Check that the DB is up
            String jdbcUrl = postgresContainer.getJdbcUrl();
            String username = postgresContainer.getUsername();
            String password = postgresContainer.getPassword();

            System.out.println("JDBC URL: " + jdbcUrl);
            System.out.println("User: " + username);
            System.out.println("Password: " + password);

            assertEquals("test", username);
        }
    }
}

This test spins up a PostgreSQL container, gets the connection URL, and shows how you can use it. Sure, it's not super useful yet, but it's a start.


2. Integration with Spring Data JPA

Now we'll tie Testcontainers to your Spring Boot app. Let's say your app uses JPA, and in tests we'll bring up a PostgreSQL container.

Configuration of application.properties for tests

Create a file application-test.properties under src/test/resources:

spring.datasource.url=jdbc:tc:postgresql:latest:///testdb
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
Heads-up:
jdbc:tc:postgresql:latest:///testdb — this is a bit of Testcontainers magic. It will automatically spin up a PostgreSQL container and hook up its data.

Example test using JPA

Suppose you have an entity User and a repository UserRepository:


@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Constructors, getters and setters
}


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

Let's create an integration test for the repository:


@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveUser() {
        // Create a new user
        User user = new User();
        user.setName("Test User");

        // Save it to the DB
        User savedUser = userRepository.save(user);

        // Check the result
        assertNotNull(savedUser.getId());
        assertEquals("Test User", savedUser.getName());
    }
}

Now, when you run the test, Spring Boot will automatically spin up a PostgreSQL container via Testcontainers.


3. Manual control of the container

In complex scenarios you might want to manage the container manually. For example:


@Testcontainers
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class ManualPostgresContainerTest {

    @Container
    private static PostgreSQLContainer
    postgresContainer = new PostgreSQLContainer<>("postgres:latest");

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

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveUserManualContainer() {
        User user = new User();
        user.setName("Another User");
        User savedUser = userRepository.save(user);

        assertNotNull(savedUser.getId());
        assertEquals("Another User", savedUser.getName());
    }
}

Here @Container manages the container lifecycle, and @DynamicPropertySource lets you register dynamic properties into the Spring context.


Common errors and how to fix them

  1. Docker isn't running If Docker isn't up, Testcontainers simply won't be able to spin up containers. Make sure Docker is running properly.
  2. Network issues Sometimes containers can't interact with the app because of network settings. Check if you're running in a restricted Docker mode (for example, with no internet).
  3. Not enough resources Databases can be heavy for a local machine. Make sure your machine has enough memory and CPU to run them.

Practical use

You just saw how Testcontainers can make life easier for developers and testers. Testing with real databases is now accessible and simple, and tests become more reliable. This tool is especially useful in interviews where it's important to show confidence in testing microservices. In real projects it helps avoid integration issues with production systems.

For further reading, check out the Testcontainers documentation.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION