Unravel โบ Java concepts โบ Testing
Testcontainers (real databases and brokers in tests)
Testcontainers starts real services (PostgreSQL, Kafka, Redis, Elasticsearch) in Docker for the duration of a test.
Learn these first
At a glance
A pop-up kitchen delivered to the test site: the real equipment, installed for the day, and taken away afterwards, instead of cooking on a toy stove that behaves differently.
- 1Declare a container as a static field: @Container static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16")With @Testcontainers it starts before tests and stops after.
- 2Wire the dynamic port into the application: @ServiceConnection (Spring Boot 3.1+) does it automatically; otherwise @DynamicPropertySource sets spring.datasource.url to db.getJdbcUrl()
- 3Share one container across the whole test class or suite (a singleton container or static field) because start-up costs seconds
- 4Modules exist for most infrastructure (Kafka, MongoDB, LocalStack for AWS, Elasticsearch, RabbitMQ), and GenericContainer runs any image with a wait strategy
- H2 or other embedded replacements hide real differences (SQL dialect, locking, JSON types, collations)
- Make the image tag explicit and equal to production (postgres:16.2), never latest
- Testcontainers talks to the Docker daemon through its API, creates the container with a random mapped host port, and waits using a strategy (log message, port, HTTP health) until the service is ready
@Testcontainers@SpringBootTestclass OrderRepositoryIT { @Container @ServiceConnection static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16"); @Autowired OrderRepository orders; @Test void savesAndFindsByStatus() { orders.save(new Order("NEW")); assertEquals(1, orders.findByStatus("NEW").size()); }}- line 1 uses Testcontainers, the core of Testcontainers (real databases and brokers in tests)
- line 4 uses Container, the core of Testcontainers (real databases and brokers in tests)
- line 5 uses ServiceConnection, the core of Testcontainers (real databases and brokers in tests)
- line 6 uses PostgreSQLContainer, the core of Testcontainers (real databases and brokers in tests)
Read the full explanation (about 1 min)
Testcontainers starts real services (PostgreSQL, Kafka, Redis, Elasticsearch) in Docker for the duration of a test. You test against the same engine as production instead of an in-memory imitation.
A mental model
A pop-up kitchen delivered to the test site: the real equipment, installed for the day, and taken away afterwards, instead of cooking on a toy stove that behaves differently.
How it works
Declare a container as a static field: @Container static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16"). With @Testcontainers it starts before tests and stops after.
Wire the dynamic port into the application: @ServiceConnection (Spring Boot 3.1+) does it automatically; otherwise @DynamicPropertySource sets spring.datasource.url to db.getJdbcUrl().
Share one container across the whole test class or suite (a singleton container or static field) because start-up costs seconds.
Modules exist for most infrastructure (Kafka, MongoDB, LocalStack for AWS, Elasticsearch, RabbitMQ), and GenericContainer runs any image with a wait strategy.
Going deeper
H2 or other embedded replacements hide real differences (SQL dialect, locking, JSON types, collations). Container tests catch migration and query bugs before production does.
Make the image tag explicit and equal to production (postgres:16.2), never latest. Pull errors or a changed image produce flaky builds.
CI needs Docker access. Use a Docker-capable runner, and consider reuse (withReuse(true)) for local development to avoid restarting containers on each run.
Under the hood
Testcontainers talks to the Docker daemon through its API, creates the container with a random mapped host port, and waits using a strategy (log message, port, HTTP health) until the service is ready. A small helper container (Ryuk) removes leftover containers if the test JVM dies.
Common mistakes
- Using
latestimage tags. - One container per test method.
- Hard-coded ports instead of the mapped port.
- Testing only against H2 when production uses another database.
How Unravel recognises it
When you paste code, Unravel looks for these signs of testcontainers (real databases and brokers in tests). Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@Testcontainers@Container@ServiceConnection - Types:
PostgreSQLContainerMySQLContainerMariaDBContainerGenericContainerMongoDBContainerRedisContainerLocalStackContainerDockerImageNameElasticsearchContainerRabbitMQContainerOracleContainerMSSQLServerContainerCassandraContainerGenericContainerand 7 more - Configuration keys:
jdbc:tc:
Part of a learning path
- Testing enterprise code ยท previous: Testing HTTP endpoints (MockMvc, WebTestClient, REST Assured) ยท next: Faking remote services (WireMock, MockWebServer, Pact)
Related concepts
Frequently asked questions
What is Testcontainers (real databases and brokers in tests)?
Testcontainers starts real services (PostgreSQL, Kafka, Redis, Elasticsearch) in Docker for the duration of a test. You test against the same engine as production instead of an in-memory imitation.
What are the most common mistakes with Testcontainers (real databases and brokers in tests)?
Using latest image tags. One container per test method. Hard-coded ports instead of the mapped port. Testing only against H2 when production uses another database.
How can I see Testcontainers (real databases and brokers in tests) in real code?
Paste your own Java into Unravel and it will point out where testcontainers (real databases and brokers in tests) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.