Testcontainers ile Integration Test: Mock, Networking ve Alias
Unit testler hızlıdır ama üretim ortamına en yakın güveni çoğu zaman integration test verir. Testcontainers, veritabanı, mesaj kuyruğu veya özel servisleri gerçek süreçler olarak ayağa kaldırarak bu güveni CI/CD içinde otomatikleştirir.
Bu yazıda Testcontainers ile integration test stratejisi, mock sınırları, networking ve network alias konularını derinlemesine ele alıyoruz.
Testcontainers Nedir?
Testcontainers, test süresince geçici Docker container’ları başlatan bir kütüphane ailesidir (Java, Go, .NET, Node.js, Python vb.). Test bitince container’lar temizlenir; yerel makinede veya CI runner’da Docker (veya uyumlu runtime) gerekir.
Neden Sadece Mock Yetmez?
Mock’lar dış dünyayı taklit eder; davranışı sizin varsayımlarınıza bağlar. Gerçek PostgreSQL sürümü, TLS, collation, timeout veya driver farkları mock’ta görünmez.
Integration test + Testcontainers:
- Gerçek protokol ve sürümle doğrular
- Migration ve SQL uyumluluğunu yakalar
- “Prod’da çalışıyordu testte geçiyordu” ayrımını daraltır
Mock vs Gerçek: Sınır Nerede Çizilir?
Mock’lamaya uygun
- Harici SaaS faturalandırma API’si (rate limit, maliyet)
- Üçüncü parti OAuth sağlayıcısı (deterministik token senaryoları)
- Zaman/saat (
Clockenjeksiyonu) - Rastgelelik (seed’li generator)
Testcontainers ile gerçek çalıştırmaya uygun
- PostgreSQL, MySQL, MongoDB
- Redis, Kafka, RabbitMQ
- Elasticsearch, MinIO (S3 uyumlu)
- Kendi servisinizin Docker imajı
Kural: Protokol ve sürüm hassasiyeti yüksek olan her şeyi mümkün olduğunca gerçek ortamda test edin; iş kuralları ve dış API sözleşmeleri için ince mock veya contract test kullanın.
Cizim: Test Piramidi ve Testcontainers Yeri
/\
/ \ E2E (az, yavaş)
/____\
/ \ Integration (Testcontainers burada güçlü)
/________\
/ \ Unit (çok, hızlı)
/____________\
Networking: Container’lar Birbirini Nasıl Görür?
Testcontainers, container’ları varsayılan olarak bridge ağına bağlar. Aynı Docker network içindeki container’lar birbirine container adı veya network alias ile erişebilir.
Cizim: Tek Network, Iki Servis
+---------------- Docker Network (test-net) ----------------+
| |
| +-------------+ DNS: postgres, db-alias |
| | PostgreSQL |<------------------------------------+ |
| | :5432 | | |
| +-------------+ | |
| ^ | |
| | jdbc/postgres://postgres:5432 | |
| | | |
| +-----+-------------+ | |
| | Uygulama / Test | | |
| | (JUnit / Go test) | | |
| +-------------------+ | |
| |
+-----------------------------------------------------------+
Uygulama test içinde localhost yerine servis hostname’i (container name veya alias) kullanmalıdır.
Network Alias Nedir, Ne İşe Yarar?
Docker’da bir container’a veya network üyeliğine ek hostname vermek için kullanılır. Özellikle:
- Uygulama config’inde sabit bir host adı bekleniyorsa (
db.internal,kafka:9092) - Aynı imajı farklı rollerle iki kez çalıştırırken ayırt etmek için
- Eski docker-compose isimleriyle uyum sağlamak için
Örnek düşünce: Test kodunuz spring.datasource.url=jdbc:postgresql://db-alias:5432/app bekliyorsa, PostgreSQL container’ına db-alias alias’ı verirsiniz; böylece prod benzeri URL’yi değiştirmeden test edersiniz.
Host Tarafından Erişim: Mapped Port
Test process’i çoğu zaman host üzerinde çalışır; container içindeki 5432 genelde localhost:RANDOM_PORT ile publish edilir. Testcontainers bu portu getMappedPort(5432) ile verir.
Dikkat: İki farklı model vardır:
- Host’tan test:
127.0.0.1:mappedPortkullanın. - Container’dan container’a: Aynı network’te iç port + DNS adı kullanın; mapped port karıştırmayın.
Paralel Test ve Izolasyon
CI’da paralel job veya aynı makinede paralel test paketleri çalışıyorsa:
- Her test sınıfı/suite için ayrı network veya benzersiz container isimleri
- Dinamik veritabanı adı veya şema (
test_${uuid}) reusemodunu CI’da genelde kapatın (temizlik ve determinizm için)
Aksi halde port çakışması veya veri sızıntısı yaşanır.
Mock + Testcontainers Hibrit Deseni
[Test]
|
+---> PostgreSQL (Testcontainers) -----> gercek SQL
|
+---> PaymentClient (mock/stub) -----> dis API yok
Veriyi gerçek tutup, pahalı veya flakeli dış çağrıları mock’lamak sık görülen üretim kalitesi desenidir.
Ornek Akis: API + DB Integration Testi
@BeforeAll/TestMain: PostgreSQL container başlat- Flyway/Liquibase veya ham SQL ile şema kur
- Uygulama bağlantı havuzunu
mappedPortveya network hostname ile yapılandır - HTTP client ile API’yi çağır; DB assert et
@AfterAll: container durdur (genelde otomatik)
Kritik Noktalar
1) CI Ortamında Docker
GitHub Actions, GitLab Runner vb. için Docker-in-Docker veya privileged socket paylaşımı gerekir. Pipeline dokümantasyonunu proje README’sinde netleştirin.
2) Baslangic Suresi
Ilk çekilen imajlar yavaş olabilir. Layer cache ve CI cache (registry mirror) ile süre kısaltılır.
3) Saglik Kontrolleri
waitFor stratejileri kullanın; port açık olsa bile servis henüz hazır olmayabilir.
4) Guvenlik
Test imajlarını sabitleyin (postgres:16-alpine@sha256:...). latest CI’da sürpriz üretir.
Sık Hatalar
- Host ve container network karıştırmak
- Paralel testlerde aynı volume/port paylaşmak
- Mock’u “gerçek” sandığı için false positive
- Alias tanımlamayı unutup config’te farklı hostname bırakmak
Sonuc
Testcontainers, integration testleri gerçekçi ve tekrarlanabilir kılar. Mock’u tamamen reddetmek şart değil; doğru sınırda kullanmak gerekir. Networking ve alias bilgisi, özellikle mikroservis ve çoklu container senaryolarında hata ayıklama süresini ciddi kısaltır.
Kısa özet: Protokolü gerçekle test et, iş kuralını ve pahalı dış dünyayı kontrollü mock’la.