Integration Testing Kotlin with Docker: From A to Z
\n\nIn modern Kotlin development, integration testing using Docker has become the de facto standard. Containerization allows you to create isolated, reproducible test environments that are as close to the production environment as possible. In this article, we will break down the complete process of setting up and writing integration tests for Kotlin applications using Docker and the Testcontainers library.
\n\nWhy is Docker important for Kotlin integration testing?
\n\nTraditional integration tests often suffer from environment issues: different database versions, driver incompatibility, flaky tests due to background processes. Docker solves these problems by providing:
\n\n- \n
- Isolation: each test runs in a clean container \n
- Reproducibility: the same environment on any machine \n
- Scalability: the ability to test complex microservice architectures \n
- Speed: fast container startup and shutdown \n
Setting up a Kotlin project for integration tests with Docker
\n\nFirst, let's add the necessary dependencies to build.gradle.kts:
dependencies {\n // Testcontainers\n testImplementation("org.testcontainers:testcontainers:1.19.3")\n testImplementation("org.testcontainers:postgresql:1.19.3")\n testImplementation("org.testcontainers:kafka:1.19.3")\n testImplementation("org.testcontainers:junit-jupiter:1.19.3")\n \n // Database\n testImplementation("org.postgresql:postgresql:42.7.1")\n \n // JUnit 5\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")\n \n // For Spring Boot applications\n testImplementation("org.springframework.boot:spring-boot-starter-test:3.2.1")\n testImplementation("org.springframework.boot:spring-boot-testcontainers:3.2.1")\n}\n\nBasic example: testing with PostgreSQL
\n\nLet's look at a simple example - testing a repository that works with PostgreSQL. Let's create an integration test using Testcontainers:
\n\nimport org.junit.jupiter.api.Test\nimport org.testcontainers.containers.PostgreSQLContainer\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\nimport javax.sql.DataSource\n\n@Testcontainers\nclass UserRepositoryIntegrationTest {\n\n companion object {\n @Container\n val postgres = PostgreSQLContainer<Nothing>("postgres:15-alpine").apply {\n withDatabaseName("testdb")\n withUsername("test")\n withPassword("test")\n }\n }\n\n @Test\n fun `should save and find user`() {\n val dataSource = createDataSource(postgres)\n val repository = UserRepository(dataSource)\n \n val user = User(id = null, name = "Ivan Petrov", email = "ivan@example.com")\n val savedUser = repository.save(user)\n \n val foundUser = repository.findById(savedUser.id!!)\n assert(foundUser != null)\n assert(foundUser?.name == "Ivan Petrov")\n }\n\n private fun createDataSource(container: PostgreSQLContainer<*>): DataSource {\n val config = HikariConfig().apply {\n jdbcUrl = container.jdbcUrl\n username = container.username\n password = container.password\n driverClassName = container.driverClassName\n }\n return HikariDataSource(config)\n }\n}\n\nTesting microservices with Kafka and Docker Compose
\n\nFor more complex scenarios requiring multiple services, use Docker Compose via Testcontainers:
\n\nimport org.testcontainers.containers.DockerComposeContainer\nimport org.testcontainers.containers.wait.strategy.Wait\nimport java.io.File\n\nclass OrderService