اختبار التكامل في Kotlin مع Docker: المستوى المتقدم
اختبار التكامل هو مرحلة أساسية للتحقق من تفاعل مكونات التطبيق. في نظام Kotlin وDocker، ترتقي هذه العملية إلى مستوى جديد بفضل الحاويات. في هذه المقالة، سنستعرض التقنيات المتقدمة لإعداد وتنفيذ اختبارات التكامل باستخدام Docker وTestcontainers وKotlin DSL.
1. الأساسيات: Testcontainers مع Kotlin DSL
Testcontainers هي مكتبة لـ Java/Kotlin تدير دورة حياة حاويات Docker داخل الاختبارات. تتوفر لـ Kotlin واجهة DSL سهلة الاستخدام تبسط التكوين.
// build.gradle.ktsdependencies { testImplementation("org.testcontainers:testcontainers:1.20.0") testImplementation("org.testcontainers:postgresql:1.20.0") testImplementation("org.jetbrains.kotlin:kotlin-test")}مثال اختبار مع PostgreSQL:
import org.testcontainers.containers.PostgreSQLContainerimport org.testcontainers.junit.jupiter.Containerimport org.testcontainers.junit.jupiter.Testcontainersimport org.junit.jupiter.api.Test
@Testcontainersclass UserRepositoryTest {
companion object { @Container val postgres = PostgreSQLContainer<Nothing>("postgres:15-alpine") .withDatabaseName("testdb") .withUsername("test") .withPassword("test") }
@Test fun `should connect to database`() { val jdbcUrl = postgres.jdbcUrl // نستخدم jdbcUrl في DataSource println("Connected to: $jdbcUrl") }}2. تشغيل الحاويات بالتوازي
لتسريع الاختبارات، يمكن تشغيل الحاويات بالتوازي. ومع ذلك، من المهم مراعاة تعارضات المنافذ. استخدم المنافذ الديناميكية والمزامنة عبر GenericContainer.
import org.testcontainers.containers.GenericContainerimport org.testcontainers.containers.Networkimport org.testcontainers.containers.wait.strategy.Waitimport java.time.Duration
class ParallelContainerTest {
companion object { private val network = Network.newNetwork()
private val redis1 = GenericContainer<Nothing>("redis:7-alpine") .withNetwork(network) .withNetworkAliases("redis1") .withExposedPorts(6379) .waitingFor(Wait.forListeningPort())
private val redis2 = GenericContainer<Nothing>("redis:7-alpine") .withNetwork(network) .withNetworkAliases("redis2") .withExposedPorts(6379) .waitingFor(Wait.forListeningPort())
init { redis1.start() redis2.start() } }
@Test fun `parallel containers test`() { val port1 = redis1.getMappedPort(6379) val port2 = redis2.getMappedPort(6379) println("Redis1 port: $port1, Redis2 port: $port2") }}استخدم Network للعزل و waitingFor لانتظار جاهزية الحاوية.
3. Docker Compose في الاختبارات
للسيناريوهات المعقدة (قاعدة بيانات + وسيط رسائل + ذاكرة تخزين مؤقت)، من المناسب استخدام DockerComposeContainer. يقوم بتحميل ملف docker-compose.yml وإدارة جميع الخدمات.
import org.testcontainers.containers.DockerComposeContainerimport java.io.File
class FullStackTest {
companion object { private val compose = DockerComposeContainer<Nothing>( File("src/test/resources/docker-compose.yml") ).apply { withExposedService("postgres_1", 5432) withExposedService("redis_1", 6379) withLocalCompose(true) start() } }
@Test fun `full integration test`() { val pgPort = compose.getServicePort("postgres_1", 5432) val redisPort = compose.getServicePort