Integration Testing Kotlin with Docker: Advanced Level
\n\nIntegration testing is a critically important stage in the development of modern microservices in Kotlin. Using Docker allows you to create isolated, reproducible environments that are as close to production as possible. In this article, we will look at advanced techniques for integration testing Kotlin applications with Docker, including Testcontainers, performance optimization, and CI/CD integration.
\n\n1. Setting up Testcontainers for a Kotlin Project
\n\nTestcontainers is a library for JUnit that manages the lifecycle of Docker containers directly from tests. For Kotlin, it provides convenient DSL extensions.
\n\n1.1. Adding Dependencies
\n\nAdd the following dependencies to build.gradle.kts:
testImplementation("org.testcontainers:testcontainers:1.19.3")\ntestImplementation("org.testcontainers:postgresql:1.19.3")\ntestImplementation("org.testcontainers:junit-jupiter:1.19.3")\ntestImplementation("org.jetbrains.kotlin:kotlin-test")\n\n1.2. Basic Example with PostgreSQL
\n\nConsider a test for a repository working with PostgreSQL:
\n\nimport org.junit.jupiter.api.Test\nimport org.testcontainers.containers.PostgreSQLContainer\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\n\n@Testcontainers\nclass UserRepositoryTest {\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 retrieve user`() {\n val dataSource = createDataSource(postgres.jdbcUrl, postgres.username, postgres.password)\n val repository = UserRepository(dataSource)\n\n val user = User(id = null, name = "Alice", email = "alice@example.com")\n val saved = repository.save(user)\n\n val found = repository.findById(saved.id!!)\n assert(found?.name == "Alice")\n }\n\n private fun createDataSource(jdbcUrl: String, username: String, password: String): DataSource {\n // HikariCP or another connection pool\n return HikariDataSource(HikariConfig().apply {\n this.jdbcUrl = jdbcUrl\n this.username = username\n this.password = password\n driverClassName = "org.postgresql.Driver"\n })\n }\n}\n\nTestcontainers automatically starts the container before tests and stops it after completion. This guarantees a clean test environment.
\n\n2. Testing Multiple Services with Docker Compose
\n\nIn real microservices, interaction with several external systems is often required: databases, message queues, caches. Use DockerComposeContainer to spin up the full stack.
2.1. Docker Compose Configuration
\n\nCreate a docker-compose-test.yml file:
version: '3.8'\nservices:\n postgres:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: testdb\n POSTGRES_USER: test\n POSTGRES_PASSWORD: test\n redis:\n image: redis:7-alpine\n rabbitmq:\n image: rabbitmq:3-management-alpine\n\n2.2. Integration Test with Docker Compose
\n\nAn example of a test verifying service interaction with a database, cache, and queue:
\n\nimport org.junit.jupiter.api.Test\nimport org.testcontainers.containers.DockerComposeContainer\nimport org.testcontainers.junit.jupiter.Container\nimport org.testcontainers.junit.jupiter.Testcontainers\nimport java.io.File\n\n@Testcontainers\nclass OrderServiceIntegrationTest {\n\n companion object {\n @Container\n val environment = DockerComposeContainer(\n File("src/test/resources/docker-com