0Pricing
Java Academy · Lesson

Testcontainers: Real Database Integration Tests

Spin up PostgreSQL in Docker with Testcontainers and run integration tests against a real database.

Testcontainers: Real Database Integration Tests is a free Java Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Testcontainers?

H2 in-memory databases don't support PostgreSQL-specific features (JSON, full-text search, ON CONFLICT). Testcontainers spins up real Docker containers during tests, giving you a production-identical database.

Adding Testcontainers Dependency

Add the Testcontainers BOM and PostgreSQL module to your build file. Spring Boot 3.1+ includes a managed Testcontainers version.

// build.gradle:
testImplementation "org.testcontainers:junit-jupiter"
testImplementation "org.testcontainers:postgresql"
// Optionally use Spring Boot Testcontainers support:
testImplementation "org.springframework.boot:spring-boot-testcontainers"

@Testcontainers and @Container

Annotate the test class with @Testcontainers and declare the container as a static @Container field. JUnit 5 starts and stops it automatically.

@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserRepositoryIntegrationTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("test");
}

Configuring DataSource from Container

Use @DynamicPropertySource to inject the container's dynamic port and credentials into Spring's DataSource configuration.

@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);
}

Spring Boot 3.1 ServiceConnection

Spring Boot 3.1+ auto-configures the DataSource from a Testcontainers @Container bean annotated with @ServiceConnection — no need for @DynamicPropertySource.

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

Singleton Container Pattern

Starting a container per test class is slow. Share one container across all tests using a static field with Startables.deepStart() or an abstract base class.

abstract class AbstractIntegrationTest {
    @Container
    static final PostgreSQLContainer<?> POSTGRES =
        new PostgreSQLContainer<>("postgres:16-alpine").withReuse(true);
    static { POSTGRES.start(); }
    @DynamicPropertySource
    static void props(DynamicPropertyRegistry r) {
        r.add("spring.datasource.url",      POSTGRES::getJdbcUrl);
        r.add("spring.datasource.username", POSTGRES::getUsername);
        r.add("spring.datasource.password", POSTGRES::getPassword);
    }
}

Testing PostgreSQL-Specific Features

With Testcontainers you can test JSON columns, JSONB operators, full-text search, and ON CONFLICT DO UPDATE — impossible with H2.

@Test
void jsonb_search_works() {
    // Insert row with JSONB column, then query
    List<Event> logins = repo.findByType("login");
    assertEquals(1, logins.size());
}

Multiple Containers: Compose

Use DockerComposeContainer to run a full stack (Postgres + Redis + Kafka) for integration tests that require multiple services.

@Container
static DockerComposeContainer<?> compose = new DockerComposeContainer<>(
    new File("src/test/resources/docker-compose.yml"))
    .withExposedService("postgres", 5432)
    .withExposedService("redis", 6379);

Ryuk: Automatic Cleanup

Testcontainers uses a Ryuk container to automatically stop and remove containers when the JVM exits — even on test failure, preventing container leaks.

@SpringBootTest with Testcontainers

Use @SpringBootTest instead of @DataJpaTest to test the full application stack (controllers, services, repositories) against a real Postgres container.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class FullIntegrationTest extends AbstractIntegrationTest {
    @LocalServerPort int port;
    @Autowired TestRestTemplate rest;
    @Test
    void end_to_end_user_creation() {
        ResponseEntity<UserDto> r = rest.postForEntity("/api/users", createReq(), UserDto.class);
        assertEquals(HttpStatus.CREATED, r.getStatusCode());
    }
}

Container Image Caching

Testcontainers caches pulled Docker images locally. Pull images in CI before tests run (docker pull postgres:16-alpine) to avoid download overhead during the test run.

Quick Check

Which annotation auto-wires a Testcontainers container to Spring's DataSource in Spring Boot 3.1+?

Recap

Testcontainers runs real Docker containers for integration tests. Use @Container + @DynamicPropertySource or @ServiceConnection (Boot 3.1+). Share containers across tests with the singleton pattern. Test PostgreSQL-specific features impossible with H2.

Frequently asked questions

Is the “Testcontainers: Real Database Integration Tests” lesson free?

Yes — the full text of “Testcontainers: Real Database Integration Tests” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Testcontainers: Real Database Integration Tests”?

Spin up PostgreSQL in Docker with Testcontainers and run integration tests against a real database. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Testcontainers: Real Database Integration Tests” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Parameterized Tests with @CsvSource and @MethodSource
  2. Mockito Advanced: Argument Captors and Spies
  3. Spring Boot Test Slices: @WebMvcTest and @DataJpaTest
  4. Testcontainers: Real Database Integration Tests
← Back to Java Academy