Testcontainers for integration tests

Docker · lesson 23 of 31 · 4 min read

Test against a real Postgres or Kafka in a throwaway container instead of against a mock.

Open this lesson in the learning hub

Key points

  • Testcontainers starts a real dependency from inside a JUnit test and hands you the random host port it landed on.
  • A random port is the point: no clashes, so the same suite runs on a laptop and on a shared CI agent at the same time.
  • In Spring Boot 3.1+ annotate the container @ServiceConnection and the datasource properties are wired for you.
  • Make the container static so it starts once per class. Container startup is the slow part, not your assertions.
  • The Ryuk sidecar removes the containers when the JVM exits, even after a crash, so nothing is left behind on the agent.
  • The one real prerequisite is a reachable Docker daemon on whatever machine runs the tests.

Example

@SpringBootTest
@Testcontainers
class OrderRepositoryTest {

    // static: started once for the whole class, not per test method
    @Container
    @ServiceConnection
    static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16-alpine");

    @Autowired OrderRepository repo;

    @Test
    void savesAndReadsBack() {
        repo.save(new Order("A-1", 3));
        assertThat(repo.findByRef("A-1")).isPresent();
    }
}

// no jdbc url, user or password anywhere: @ServiceConnection supplies them

A real database in the test beats a mock that agrees with you.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Docker course, and every lesson in it is listed on the Docker contents page.