{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiax4h4npjoh4bdsq4xnv5z566rddoip2zdndlocpmu7cvo5iupkiq",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mommyd643lc2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreichjrlnmgmb3vwxmfigopnnveomv66exrjrjzqabcsud2rwkqcbrm"
    },
    "mimeType": "image/webp",
    "size": 72780
  },
  "path": "/nitish_95c3a169c005f1d4fe/spring-boot-32-testcontainers-reliable-integration-testing-with-real-dependencies-5bfo",
  "publishedAt": "2026-06-19T05:21:27.000Z",
  "site": "https://dev.to",
  "tags": [
    "java",
    "springboot",
    "programming",
    "tutorial",
    "https://gumroad.com",
    "@Testcontainers",
    "@SpringBootTest",
    "@Container",
    "@Autowired",
    "@Test",
    "@DynamicPropertySource"
  ],
  "textContent": "#  Spring Boot 3.2 + Testcontainers: Reliable Integration Testing with Real Dependencies\n\nWithout Testcontainers, your integration tests might pass locally with H2 but fail in CI when connecting to a mismatched PostgreSQL version. Production database migrations succeed in development but break when applied to a different database engine.\n\n##  Prerequisites\n\n  * Java 17 or later\n  * Spring Boot 3.2.x\n  * Docker Engine 24.0+ with Docker Compose\n  * JUnit Jupiter 5.9+\n  * Testcontainers 1.19.3\n\n\n\n##  Configuring Testcontainers for Database Testing\n\nTestcontainers provides disposable database instances for integration tests. Add these dependencies to your `pom.xml`:\n\n\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>3.2.4</version>\n    </parent>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <version>1.19.3</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>postgresql</artifactId>\n            <version>1.19.3</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n\nDisable the default datasource in test configuration:\n\n\n\n    # src/test/resources/application.properties\n    spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver\n    spring.datasource.url=jdbc:tc:postgresql:15-alpine:///test?TC_DAEMON=true\n    spring.flyway.enabled=false\n\n\n##  Writing a Containerized Integration Test\n\nThis test verifies database schema initialization using a real PostgreSQL instance. The container starts before tests and destroys itself afterward:\n\n\n\n    package com.example.orderservice;\n\n    import org.junit.jupiter.api.Test;\n    import org.springframework.beans.factory.annotation.Autowired;\n    import org.springframework.boot.test.context.SpringBootTest;\n    import org.springframework.jdbc.core.JdbcTemplate;\n    import org.testcontainers.containers.PostgreSQLContainer;\n    import org.testcontainers.junit.jupiter.Container;\n    import org.testcontainers.junit.jupiter.Testcontainers;\n\n    import static org.assertj.core.api.Assertions.assertThat;\n\n    @Testcontainers\n    @SpringBootTest\n    class OrderRepositoryIntegrationTest {\n\n        @Container\n        static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(\"postgres:15-alpine\")\n            .withDatabaseName(\"test\")\n            .withUsername(\"test\")\n            .withPassword(\"test\");\n\n        @Autowired\n        private JdbcTemplate jdbcTemplate;\n\n        @Test\n        void should_connect_to_live_database() {\n            Integer result = jdbcTemplate.queryForObject(\"SELECT 1\", Integer.class);\n            assertThat(result).isEqualTo(1);\n        }\n    }\n\n\nRun the test with Docker Desktop active. Testcontainers will download the PostgreSQL image on first execution and reuse it for subsequent runs.\n\n##  Managing Container Lifecycles\n\nDefine a base test class to share containers across multiple test classes:\n\n\n\n    package com.example.shared;\n\n    import org.springframework.test.context.DynamicPropertyRegistry;\n    import org.springframework.test.context.DynamicPropertySource;\n    import org.testcontainers.containers.PostgreSQLContainer;\n    import org.testcontainers.junit.jupiter.Container;\n    import org.testcontainers.junit.jupiter.Testcontainers;\n\n    @Testcontainers\n    public abstract class BaseIntegrationTest {\n\n        @Container\n        static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(\"postgres:15-alpine\")\n            .withReuse(true);\n\n        @DynamicPropertySource\n        static void configureProperties(DynamicPropertyRegistry registry) {\n            registry.add(\"spring.datasource.url\", postgres::getJdbcUrl);\n            registry.add(\"spring.datasource.username\", postgres::getUsername);\n            registry.add(\"spring.datasource.password\", postgres::getPassword);\n        }\n    }\n\n\n##  Common Mistakes\n\n**Mistake 1: Non-static container field causing multiple instances**\n\n\n\n    @Container // Missing static modifier\n    PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>();\n\n\n\n    @Container\n    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>();\n\n\nTestcontainers requires static fields for class-level containers. Non-static fields create new containers per test method.\n\n**Mistake 2: Hardcoded JDBC URL in test properties**\n\n\n\n    spring.datasource.url=jdbc:postgresql://localhost:5432/test\n\n\n\n    @DynamicPropertySource\n    static void registerProperties(DynamicPropertyRegistry registry) {\n        registry.add(\"spring.datasource.url\", postgres::getJdbcUrl);\n    }\n\n\nHardcoded URLs bypass container networking. Use `@DynamicPropertySource` to inject runtime connection details.\n\n**Mistake 3: Missing container reuse configuration**\n\n\n\n    new PostgreSQLContainer<>(\"postgres:15-alpine\");\n\n\n\n    new PostgreSQLContainer<>(\"postgres:15-alpine\")\n        .withReuse(true);\n\n\nWithout reuse, Testcontainers destroys containers after each test class. Enable reuse in `.testcontainers.properties` and chain `withReuse(true)`.\n\n##  Summary\n\n  * Replace in-memory databases with Testcontainers-managed PostgreSQL instances using `@Testcontainers` and `@Container`\n  * Inject dynamic connection properties using `@DynamicPropertySource` registry\n  * Enable container reuse with `.withReuse(true)` and `testcontainers.reuse.enable=true` in `.testcontainers.properties`\n\n\n\n_The author publishes Spring Boot starter templates at https://gumroad.com_",
  "title": "Spring Boot 3.2 + Testcontainers: Reliable Integration Testing with Real Dependencies"
}