Strong answer
Testcontainers starts real dependencies such as PostgreSQL, Kafka, or Redis in throwaway Docker containers from the test, so integration tests exercise the same engines that run in production. Docker then packages the service into an immutable image built once and promoted through environments. A CI/CD pipeline ties it together: compile, run unit and integration tests, scan, build the image, tag it with the commit, deploy progressively, and roll back on failing health signals.
Java example
1@SpringBootTest
2@Testcontainers
3class OrderRepositoryIT {
4
5 @Container
6 @ServiceConnection
7 static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");
8
9 @Autowired OrderRepository orders;
10
11 @Test
12 void findsRecentOrdersForCustomer() {
13 orders.save(TestOrders.placedBy(7L));
14 assertThat(orders.findRecent(7L, PageRequest.of(0, 10))).hasSize(1);
15 }
16}
17
18# Dockerfile
19FROM eclipse-temurin:21-jdk AS build
20WORKDIR /app
21COPY . .
22RUN ./mvnw -q -DskipTests package
23
24FROM eclipse-temurin:21-jre
25RUN useradd --system app
26USER app
27COPY --from=build /app/target/orders.jar /app/orders.jar
28ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "-jar", "/app/orders.jar"]