Mikro servis mimarisinde her servis bağımsız deploy edilir; bu özgürlük, servisler arası entegrasyonun sessizce kırılmasına da zemin hazırlar. Tüketici ekip Order API yanıtında totalAmount alanının sayısal olduğunu varsayarken, sağlayıcı ekip alanı string'e çevirebilir. Entegrasyon testleri tüm sistemi ayağa kaldırmayı gerektirir; birim testleri ise sözleşmeyi doğrudan doğrulamaz. Contract testing, bu boşluğu consumer-driven sözleşmelerle doldurur: tüketici beklentisini makine okunabilir formda yazar, sağlayıcı bu sözleşmeye karşı kendi testlerini çalıştırır.
Entegrasyon testi ile contract test farkı
Entegrasyon testi gerçek ağ çağrısı, gerçek veritabanı ve gerçek bağımlılıklar kullanır. Ortam kurulumu ağırdır, testler yavaştır ve flaky olma eğilimi yüksektir. Contract test ise yalnızca HTTP isteği/yanıt şemasını, durum kodlarını ve header beklentilerini doğrular. Sağlayıcı tarafında mock sunucu, tüketici tarafında mock istemci kullanılır; gerçek servis ayağa kalkmaz.
- Hız: Contract testler saniyeler içinde tamamlanır
- İzolasyon: Bir servisin CI'si diğerinin uptime'ına bağlı değildir
- Erken uyarı: Breaking change sağlayıcı merge öncesinde yakalanır
- Dokümantasyon: Sözleşme dosyası canlı API dokümantasyonu görevi görür
Consumer-driven contract testing (CDCT)
Geleneksel provider-driven yaklaşımda API sahibi OpenAPI spec yazar; tüketiciler buna uyar. CDCT'te tüketici "ben şu isteği gönderiyorum, şu yanıtı bekliyorum" der ve bu beklenti sözleşme dosyasına dönüşür. Sağlayıcı, tüm tüketici sözleşmelerini birleştirip kendi davranışını doğrular. Bu model, gerçek kullanım senaryolarının spec'e yansımasını garanti eder.
Pact dosya yapısı
Pact, contract testing alanında en yaygın açık kaynak framework'tür. Tüketici testi çalıştığında pacts/ klasörüne JSON sözleşme yazılır. Bu dosya istek matcher'ları ve yanıt gövdesi şemasını içerir.
{
"consumer": { "name": "OrderService" },
"provider": { "name": "PaymentService" },
"interactions": [{
"description": "create payment for order",
"request": {
"method": "POST",
"path": "/payments",
"body": { "orderId": "abc-123", "amount": 150.00 }
},
"response": {
"status": 201,
"body": { "paymentId": "pay-456", "status": "pending" }
}
}]
}
Pact ile tüketici testi (.NET örneği)
ASP.NET Core tüketici servisinde PactNet kütüphanesi kullanılır. Test, gerçek HTTP istemcisi yerine Pact mock sunucusuna istek gönderir.
var pact = Pact.V3("OrderService", "PaymentService", config);
await pact.UponReceiving("a request to create payment")
.Given("order abc-123 exists")
.WithRequest(HttpMethod.Post, "/payments")
.WithJsonBody(new { orderId = "abc-123", amount = 150.00m })
.WillRespond()
.WithStatus(HttpStatusCode.Created)
.WithJsonBody(new { paymentId = Match.Type("pay-456"), status = "pending" });
await pact.VerifyAsync(async ctx => {
var client = new PaymentClient(ctx.MockServerUri);
var result = await client.CreateAsync("abc-123", 150.00m);
Assert.Equal("pending", result.Status);
});
Matcher kullanımı
Sözleşmede sabit değer yerine matcher kullanmak, sağlayıcının farklı ama uyumlu yanıtlar vermesine izin verir. Match.Type("string") herhangi bir string kabul eder; Match.Regex("pay-[0-9]+") desen tabanlı doğrulama yapar. Aşırı katı matcher breaking change riskini artırır; aşırı gevşek matcher anlamsız sözleşme üretir.
Sağlayıcı doğrulama (provider verification)
Tüketici sözleşmeleri Pact Broker'a publish edilir. Sağlayıcı CI pipeline'ında pact-verifier çalıştırılır; gerçek uygulama ayağa kaldırılır ve her sözleşme etkileşimi replay edilir. Yanıt sözleşmeyle uyuşmazsa build kırılır. Bu, sağlayıcının bilinçli veya bilinçsiz breaking change yapmasını engeller.
- Tüketici testi çalışır, sözleşme üretilir
- Sözleşme Pact Broker'a yüklenir (versiyon etiketi ile)
- Sağlayıcı CI, broker'dan ilgili sözleşmeleri çeker
- Provider verification testi her etkileşimi doğrular
- Başarısızlık durumunda deploy kapısı kapanır
Pact Broker ve can-i-deploy
Broker sadece depolama değildir; sözleşme versiyonları, ortam etiketleri ve webhook entegrasyonu sunar. can-i-deploy komutu, belirli bir servis versiyonunun belirli bir ortama deploy edilip edilemeyeceğini sorar. Tüm tüketici-sağlayıcı çiftleri doğrulanmışsa deploy izni verilir.
pact-broker can-i-deploy \
--pacticipant OrderService \
--version $GIT_SHA \
--to-environment production
Async mesajlaşma sözleşmeleri
Contract testing yalnızca senkron HTTP ile sınırlı değildir. Kafka, RabbitMQ veya Azure Service Bus üzerinden giden olaylar için message pact tanımlanır. Tüketici "OrderCreated olayında orderId ve total alanlarını bekliyorum" der; sağlayıcı bu olayı publish ederek doğrular.
Olay şeması evrimi
Olay tabanlı sistemlerde geriye dönük uyumluluk kritiktir. Yeni alan eklemek genellikle güvenlidir; alan silmek veya tip değiştirmek breaking change'dir. Schema registry (Avro, Protobuf) ile contract test birlikte kullanıldığında çift katmanlı koruma sağlanır.
Organizasyonel uygulama
Contract testing'i başarıyla benimseyen ekipler birkaç ortak pratik paylaşır. Sözleşme değişikliği code review sürecine dahil edilir. Breaking change gerektiğinde tüketici ekipleri önceden bilgilendirilir ve geçiş süresi planlanır. Pact Broker webhook'u Slack'e "PaymentService v2.3 sözleşme ihlali" bildirimi gönderir.
- Her PR'da tüketici contract testi zorunlu
- Sağlayıcı main branch'inde provider verification zorunlu
- Sözleşme dosyaları Git'te versiyonlanır (broker yedek)
- Cross-team sözleşme review toplantısı sprint başında
Yaygın tuzaklar
Contract test entegrasyon testinin yerini almaz; ikisi birlikte çalışır. Contract test protokol uyumluluğunu doğrular; entegrasyon test iş akışı mantığını doğrular. Bir diğer tuzak, sözleşmeyi çok geniş tutmaktır: tüm alanları wildcard matcher ile tanımlamak hiçbir şeyi doğrulamaz.
Flaky contract test
Provider verification ortamında test verisi tutarsızsa testler flaky olur. Pact given state provider'a veri hazırlama talimatı verir. Her state için sağlayıcı test fixture'ı tanımlanmalıdır.
Contract test vs API spec
OpenAPI spec statik dokümantasyondur; contract test canlı davranışı doğrular. İdeal kurulumda OpenAPI spec otomatik contract testten veya tam tersi üretilir. Spec ile sözleşme arasında drift oluşursa CI kırılır. Bu yaklaşım dokümantasyonun güncel kalmasını garanti eder.
Metrikler ve olgunluk
Contract testing olgunluğunu ölçmek için şu metrikler izlenir: sözleşme kapsama oranı (kaç servis çifti sözleşmeli), verification başarı oranı, breaking change yakalanma süresi (sözleşme ihlali ile fix arası), can-i-deploy red oranı. Bu metrikler QA ve platform ekibinin ortak dashboard'unda görünür olmalıdır.
Gerçek dünya senaryosu
Bir e-ticaret platformunda InventoryService, ProductCatalog'dan stok bilgisi çeker. Catalog ekibi stockLevel alanını kaldırıp availability enum'una geçer. Contract test, InventoryService CI'sinde anında kırılır; Catalog ekibi merge yapmadan önce tüketici güncellemesini koordine eder. Üretimde sessiz entegrasyon hatası yaşanmaz.
Contract testing kontrol listesi
Contract testing programını değerlendirirken şu maddeler gözden geçirilir.
- Tüm servis çiftleri için sözleşme tanımlı mı?
- Provider verification her deploy öncesi çalışıyor mu?
- Pact Broker webhook entegrasyonu aktif mi?
- Breaking change koordinasyon süreci dokümante mi?
- Async message pact gerektiren olaylar kapsandı mı?
- can-i-deploy production kapısına bağlı mı?
Provider state yönetimi
Provider verification sırasında her interaction belirli bir veri durumu gerektirir. Pact state handler, test öncesi veritabanına seed verisi yükler veya mock servisi yapılandırır. State tanımı belirsizse verification ortamında flaky sonuçlar oluşur. Her state için izole fixture ve teardown prosedürü dokümante edilmelidir.
Bi-directional contract testing
Geleneksel CDCT tüketici odaklıdır. Bi-directional yaklaşımda OpenAPI spec ve Pact sözleşmesi karşılıklı doğrulanır. Spec'ten otomatik contract üretimi ile manuel spec drift riski azalır. Büyük organizasyonlarda her iki yön birlikte kullanılarak spec-sözleşme tutarlılığı sağlanır.
Contract testing, mikro servis ekosistemlerinde güvenilir teslimatın temel taşlarından biridir. Pact veya benzeri araçlarla consumer-driven sözleşmeler kurulduğunda, ekipler bağımsız hareket ederken entegrasyon bütünlüğü korunur. Yatırım ilk kurulumda görünür; uzun vadede entegrasyon debug süresini ve üretim olaylarını dramatik biçimde azaltır.