
Backend Test Stratejileri: Birim (Unit) ve Entegrasyon Testleri
Diyarbakır Yazılım
16.08.2026
#Yazılım#Teknoloji#Topluluk
Backend geliştirmede test yazmak yalnızca hataları daha erken yakalamak için yapılan bir işlem değildir. İyi kurulmuş bir test stratejisi, geliştiricinin kodu güvenle değiştirmesine, refactor yapmasına, yeni özellik eklemesine ve production sorunlarını daha hızlı çözmesine yardım eder. On yıllık backend geliştirme pratiğinde en sık gördüğüm sorun, ekiplerin çok sayıda test yazmasına rağmen gerçek sistem sınırlarında yeterince güven oluşturamamasıdır. Backend Test Stratejileri: Birim (Unit) ve Entegrasyon Testleri konusu bu nedenle test sayısından önce hangi riski hangi test seviyesinde doğruladığımızı anlamayı gerektirir. Bu rehberde backend unit ve integration testleri nasıl yazılır, backend uygulamalarda birim testi ve entegrasyon testi farkları nelerdir, API veritabanı ve servis entegrasyon testleri nasıl yapılır ve backend test stratejisinde mocking test coverage ve test pyramid nasıl uygulanır sorularını production odaklı örneklerle ele alacağız.
Backend Testing Nedir?
Backend testing, uygulamanın business logic, API, database, authorization, harici servis, queue ve background job gibi bileşenlerinin beklenen davranışı ürettiğini doğrulama sürecidir. Burada önemli olan her kod satırını ayrı ayrı test etmek değil, hatanın kullanıcıya veya başka bir sisteme ulaşmadan önce yakalanmasını sağlayacak güvenilir bir test portföyü oluşturmaktır. Bazı davranışlar hızlı unit testlerle doğrulanırken database constraint veya HTTP middleware gibi gerçek sınırlar integration test gerektirir. Kritik kullanıcı akışları için daha az sayıda end-to-end veya smoke test kullanılabilir. Sağlıklı bir backend test stratejisi bu seviyelerin her birini doğru risk için kullanır.
Backend Uygulamalarında Neler Test Edilir?
Backend test kapsamı yalnızca service veya controller class'larıyla sınırlı değildir. Business kuralları, veri kalıcılığı, HTTP contract'ları, authentication, authorization ve dış bağımlılıklarla iletişim ayrı risk alanlarıdır. Queue mesajlarının doğru üretilmesi, cache invalidation veya dosya yükleme davranışı da production hatasına yol açabilir. Bu nedenle önce sistemin gerçek sınırlarını çıkarmak, ardından her sınır için uygun test seviyesini belirlemek gerekir. Bu yaklaşım, bütün kodu aynı test tekniğiyle doğrulamaya çalışmaktan daha sürdürülebilir sonuç verir.
Business Logic
Business logic çoğu backend uygulamasının en hızlı ve en ayrıntılı test edilmesi gereken bölümüdür. Fiyat hesaplama, indirim kuralları, durum geçişleri, üyelik koşulları veya ödeme kararları buna örnek verilebilir. Bu davranışlar mümkün olduğunca database ve network bağımlılığından ayrıldığında unit testler çok hızlı çalışır. Edge case sayısı yüksek business kuralları parameterized veya property-based testlerle de desteklenebilir. Amaç class'ın iç yapısını değil dışarıdan gözlemlenen business davranışını doğrulamaktır.
API
API testlerinde routing, HTTP method, request binding, validation, authentication ve response serialization birlikte değerlendirilebilir. Controller method'unu doğrudan çağırmak gerçek HTTP pipeline davranışını göstermediği için her zaman yeterli değildir. Özellikle middleware ve authorization kullanan projelerde endpoint integration testleri daha değerli olabilir. Status code, response body ve error contract açık biçimde doğrulanmalıdır. API dokümantasyonu ile implementation arasındaki uyum da contract testlerle korunabilir.
Database
Database testlerinde repository query'leri, ORM mapping, transaction, unique constraint ve foreign key davranışı gerçek engine üzerinde doğrulanmalıdır. Production PostgreSQL kullanırken test ortamında yalnızca fake repository veya farklı SQL engine kullanmak önemli farkları saklayabilir. ORM tarafından üretilen sorgunun gerçek database üzerinde çalışması integration testin temel değerlerinden biridir. Migration ve schema değişiklikleri de test edilmelidir. Database testi yalnızca “kayıt kaydedildi mi?” sorusundan daha geniş ele alınmalıdır.
Authentication
Authentication testlerinde anonymous request, geçerli kimlik, invalid token ve expired token senaryoları bulunmalıdır. Authentication middleware'in gerçek HTTP pipeline içinde çalışması önemlidir. Unit test seviyesinde token parser gibi saf logic ayrıca test edilebilir. Ancak endpoint'in gerçekten korunduğunu integration test daha güvenilir biçimde gösterir. Özellikle yeni route eklendiğinde authentication attribute veya middleware unutulması ciddi güvenlik problemi doğurabilir.
External Services
Harici servis entegrasyonları network, timeout ve contract değişikliği gibi kontrolünüz dışındaki riskler taşır. Unit testlerde client boundary stub veya mock ile kontrol edilebilir. Integration testlerde local HTTP stub server kullanmak gerçek request serialization ve response parsing davranışını doğrular. Production third-party API'ye her testte istek göndermek testleri yavaş ve kırılgan hale getirebilir. Contract ve failure senaryolarını kontrollü ortamda doğrulamak daha sağlıklı bir yaklaşımdır.
Queue ve Background Jobs
Queue ve background worker testleri publish, consume, retry ve idempotency davranışlarını kapsamalıdır. Bir event'in doğru payload ile yayınlanması unit veya narrow integration seviyesinde doğrulanabilir. Gerçek broker kullanıldığında acknowledgement, dead letter queue ve serialization gibi konular test edilebilir. Worker'ın aynı mesajı iki kez aldığında aynı side effect'i iki kez üretmemesi özellikle önemlidir. Scheduled job'larda zaman bağımlılığı controlled clock ile test edilebilir.
Otomatik Testler Neden Gereklidir?
Otomatik testler ekiplerin aynı doğrulamayı her değişiklikte hızlı ve tekrarlanabilir biçimde yapmasını sağlar. Manual test yalnızca belirli senaryolar için değerli olabilir, ancak yüzlerce business kuralını her commit sonrasında elle kontrol etmek sürdürülebilir değildir. Testler refactor sırasında beklenmeyen davranış değişikliklerini erken gösterir. CI/CD pipeline içinde çalışan suite, merge edilmeden önce teknik kalite için ortak bir kontrol noktası oluşturur. İyi bir test seti aynı zamanda sistem davranışının çalışan dokümantasyonu gibi görev yapar.
Regression Nedir?
Regression daha önce çalışan bir davranışın yeni değişiklik sonrasında bozulmasıdır. Bir bug düzeltildiğinde aynı sorun ileride tekrar oluşabiliyorsa test stratejisinde eksik bir koruma alanı bulunur. Bu nedenle production bug sonrası ilk işlerden biri problemi tekrar üreten otomatik test yazmaktır. Test önce fail etmeli, ardından fix uygulandığında geçmelidir. Böylece aynı bug'ın gelecekte sessizce geri dönmesi daha zor hale gelir.
Testlerin CI/CD'deki Rolü
CI/CD pipeline testleri yalnızca deployment öncesi son kontrol olarak görmemelidir. Hızlı testler geliştiriciye merge request sırasında erken feedback verir. Daha ağır integration ve contract testleri sonraki stage'lerde çalışabilir. Kritik smoke testler deployment sonrasında sistemin temel akışlarının ayakta olduğunu doğrulayabilir. Test suite'in pipeline'a yerleşimi feedback süresi ve risk seviyesine göre planlanmalıdır.
Backend Test Stratejisi Nasıl Oluşturulur?
Backend test stratejisi oluştururken ilk soru “kaç unit test yazacağız?” olmamalıdır. Önce sistemde hata oluştuğunda en yüksek etkiye sahip alanları ve dış dependency sınırlarını belirlemek gerekir. Daha sonra her davranış için en düşük maliyetle yeterli güven sağlayacak test tipi seçilir. Business logic çoğunlukla unit test ile, database ve HTTP boundary ise integration test ile daha doğru doğrulanır. Test portföyünü bu risk haritasına göre kurmak sabit yüzde hedeflerinden daha kullanışlıdır.
Sistem Risklerini Belirlemek
Test stratejisinin başlangıç noktası production risklerinin çıkarılmasıdır. Para hareketi, authorization, veri kaybı ve tekrar eden side effect gibi alanlar yüksek önceliğe sahiptir. Basit admin ekranındaki trivial getter ile ödeme idempotency davranışını aynı test ağırlığıyla ele almak doğru değildir. Geçmiş production incident'ları risk haritasını güçlendiren önemli veridir. Test yatırımı hatanın etkisine ve görülme olasılığına göre dağıtılmalıdır.
Kritik Business Flow'ları Belirlemek
Kullanıcı kayıt, ödeme, sipariş, authentication veya rapor üretme gibi kritik akışlar açık biçimde listelenmelidir. Bu akışların business kuralları unit test seviyesinde ayrıntılı korunabilir. Sistem bileşenlerinin birlikte çalıştığını göstermek için daha az sayıda integration ve E2E test eklenebilir. Her permutation'ı E2E seviyesinde tekrar etmek gerekli değildir. Aynı davranışın uygun test seviyesinde tek kaynağa yakın doğrulanması bakım maliyetini azaltır.
External Boundary'leri Çıkarmak
Database, REST API, message broker, filesystem, object storage ve third-party servisler önemli integration boundary'leridir. Serialization ve transaction davranışı da component sınırı olarak değerlendirilmelidir. Bu alanlarda fake dependency kullanıldığında production davranışı yeterince temsil edilmeyebilir. Testcontainers veya local stub server gibi araçlar gerçeğe yakın test ortamı sağlayabilir. Boundary listesi test planında açıkça bulunmalıdır.
Test Seviyelerini Belirlemek
Her davranışı mümkün olan en düşük seviyede güvenilir biçimde test etmek iyi bir başlangıç prensibidir. Saf business rule için unit test yeterliyse HTTP E2E test yazmaya gerek olmayabilir. ORM query gerçek database semantics'e bağlıysa unit mock yerine integration test daha değerlidir. Contract değişikliği servisler arası uyumluluğu etkiliyorsa contract test kullanılabilir. Test seviyesi test edilen riske göre seçilmelidir.
Feedback Süresi Hedefi Belirlemek
Test suite çok yavaşsa geliştiriciler testleri daha az çalıştırmaya başlar. Unit testlerin çoğu saniyeler içinde tamamlanmalıdır. Fast integration testler merge request pipeline içinde kabul edilebilir sürede çalışabilir. Daha ağır suite main branch veya nightly pipeline'a taşınabilir. Feedback hedefi test coverage kadar önemli bir kalite ölçümüdür.
Test Ownership Tanımlamak
Testler sahipsiz bırakıldığında flaky veya başarısız testler zamanla normal kabul edilmeye başlanır. Her test alanının sorumlu ekip veya modülle ilişkilendirilmesi faydalıdır. Pipeline'da flaky hale gelen test için owner ve düzeltme süresi belirlenebilir. Production code review kadar test kodu review'u da önemlidir. Test ownership kalite sorumluluğunu yalnızca QA rolüne bırakmayan takım kültürü oluşturur.
Unit Test Nedir?
Unit test küçük bir davranış birimini hızlı ve kontrollü ortamda doğrulayan otomatik testtir. Bu birim her zaman tek method veya tek class olmak zorunda değildir. Benim tercih ettiğim yaklaşım, unit kavramını “unit of behavior” olarak düşünmektir. Birden fazla küçük domain object birlikte tek business davranışı üretiyorsa bunları birlikte test etmek çoğu zaman daha okunabilir sonuç verir. Unit testin asıl değeri hızlı feedback ve net hata lokalizasyonudur.
Unit Kavramı Ne Anlama Gelir?
Unit, testin gözlemlediği en küçük anlamlı davranış sınırıdır. Bazı projelerde tek function olabilir, bazı domain modellerinde birkaç object birlikte bir unit oluşturabilir. Testi class sayısına göre tasarlamak implementation detail'e aşırı bağlanmaya yol açabilir. Davranışa göre tasarlanan testler refactor sırasında daha az gereksiz kırılır. Bu nedenle “hangi class'ı test edeceğim?” yerine “hangi business davranışını koruyacağım?” sorusu daha verimlidir.
Bir Unit Her Zaman Bir Method veya Class mıdır?
Hayır, unit testin sınırı teknik yapıdan çok davranışla belirlenebilir. Bir order ve discount policy birlikte fiyat hesaplıyorsa ikisini aynı test senaryosunda kullanmak yanlış değildir. Her internal class'ı mocklayıp yalnızca tek method bırakmak testin production davranışından uzaklaşmasına neden olabilir. Özellikle domain model içindeki hızlı ve deterministic object'leri gerçek haliyle birlikte çalıştırmak faydalıdır. Bu yaklaşım testlerin refactor toleransını yükseltir.
Unit of Behavior Yaklaşımı
Unit of behavior yaklaşımı implementation yerine dışarıdan gözlemlenen sonucu merkeze alır. Örneğin “stok yetersizse sipariş onaylanamaz” bir davranıştır. Bu davranışın hangi private method veya helper üzerinden gerçekleştiği test açısından ikincildir. Kod refactor edildiğinde davranış aynı kaldığı sürece testin geçmesi beklenir. Böyle testler hem dokümantasyon değeri taşır hem bakım maliyetini azaltır.
Unit Test'in Temel Özellikleri
İyi unit test hızlı, deterministic ve kolay teşhis edilebilir olmalıdır. Test sonucu saat, network veya random dependency nedeniyle değişmemelidir. Aynı test aynı kodla tekrar çalıştırıldığında aynı sonucu üretmelidir. Failure mesajı hangi business davranışının bozulduğunu açık biçimde göstermelidir. Unit testin küçük olması önemli olsa da anlamlı davranışı bölmeyecek kadar bütünlük taşıması gerekir.
Fast
Unit testler network veya gerçek database kullanmadığı için çok hızlı çalışmalıdır. Binlerce testin birkaç saniye içinde tamamlanması geliştirici feedback kalitesini artırır. Her save veya commit sonrasında çalıştırılabilecek testler refactor güveni sağlar. Yavaş unit test çoğu zaman gizli I/O veya ağır setup işaretidir. Test süresi düzenli ölçülebilir.
Isolated
Isolation testin başka testlerin state'ine veya execution order'ına bağlı olmamasıdır. Bir test önceki test tarafından oluşturulan global veriyle çalışmamalıdır. External dependency kontrol altında tutulmalıdır. Bununla birlikte isolated olmak tüm internal object'lerin mocklanması anlamına gelmez. Önemli olan testin bağımsız çalışabilmesidir.
Deterministic
Deterministic test aynı input ve kod için aynı sonucu üretir. Sistem saati, random sayı veya network response kontrol edilmezse test zaman zaman farklı davranabilir. Clock ve random generator injection bu sorunu çözer. Concurrency testlerinde bile synchronization noktaları açık biçimde tasarlanmalıdır. Deterministic test suite flaky test oranını ciddi biçimde azaltır.
Repeatable
Test developer bilgisayarında ve CI ortamında aynı davranışı göstermelidir. Local timezone veya işletim sistemi farkı test sonucunu değiştirmemelidir. Dependency version'ları kontrol altında tutulmalıdır. Test setup otomatik olmalıdır. Repeatability yeni ekip üyesinin test suite'i güvenle çalıştırabilmesini sağlar.
Easy to Diagnose
Bir test başarısız olduğunda hangi davranışın bozulduğu hızlı anlaşılmalıdır. Çok fazla assertion ve farklı senaryo tek testte toplandığında teşhis zorlaşır. Test adı ve assertion mesajları business sonucunu göstermelidir. Gereksiz mock interaction failure'ları gerçek problemi gizleyebilir. İyi test geliştiriciye neyin değiştiğini doğrudan anlatır.
Unit Test'te Neler Test Edilmelidir?
Unit testler özellikle business rules, validation, calculation ve conditional logic gibi hızlı değerlendirilebilen davranışlarda güçlüdür. Error handling ve edge case'ler de burada ayrıntılı biçimde test edilebilir. Unit test seviyesinde birçok input varyasyonu çalıştırmak integration veya E2E seviyesine göre daha ucuzdur. Bu nedenle business permutation'ların büyük bölümü unit testlerde bulunmalıdır. Böylece üst test katmanları yalnızca boundary ve sistem entegrasyonuna odaklanabilir.
Business Rules
Business rules uygulamanın gerçek değer üreten kurallarını temsil eder. İndirim şartı, limit kontrolü veya sipariş onay kuralı doğrudan unit test adayıdır. Her kural için happy path kadar reddedilen durumlar da test edilmelidir. Test adı business dilini kullanırsa okunabilirlik artar. Bu testler refactor sırasında domain davranışını koruyan temel güvenlik ağı olur.
Validation
Validation testleri geçerli ve geçersiz input sınırlarını doğrular. Null, empty, length ve format durumları parameterized testlerle verimli biçimde ele alınabilir. Framework validation mekanizmasının kendisini test etmek yerine kendi kuralınızı test etmek gerekir. HTTP binding veya annotation integration'ı gerekiyorsa API integration test daha uygun olabilir. Validation seviyesini doğru seçmek duplication riskini azaltır.
Calculations
Fiyat, vergi, puan ve ücret hesaplamaları unit test için ideal davranışlardır. Boundary ve rounding durumları açıkça test edilmelidir. Floating point veya decimal davranışı business requirement ile uyumlu olmalıdır. Aynı hesaplamayı E2E testlerde onlarca kez tekrar etmeye gerek yoktur. Saf hesaplama function'ları çok hızlı geniş coverage sağlar.
State Transitions
Order status veya subscription lifecycle gibi state machine davranışları unit testlerle iyi korunur. Hangi durumdan hangi duruma geçilebildiği açık test case'leriyle gösterilebilir. Geçersiz geçişlerin reddedildiği de doğrulanmalıdır. Event üretimi varsa domain event collection state üzerinden kontrol edilebilir. Database persist davranışı ayrı integration testin konusu olabilir.
Conditional Logic
Branch yoğun business logic unit testlerde ayrıntılı ele alınmalıdır. Farklı role, plan veya ürün tipine göre davranış değişiyorsa parameterized test faydalıdır. Branch coverage burada yardımcı sinyal olabilir. Ancak yalnızca satıra girildiğini görmek yeterli değildir, beklenen outcome doğrulanmalıdır. Testler implementation branch sayısından çok business scenarios üzerine kurulmalıdır.
Error Handling
Domain veya service logic belirli durumda hata üretmeliyse bu behavior açıkça test edilmelidir. Hata tipi ve gerekli metadata doğrulanabilir. Framework exception serialization unit testin konusu değildir. Retry veya network failure gibi boundary hataları integration seviyesinde ayrıca test edilebilir. Error path'lerin ihmal edilmesi production sorunlarının en yaygın kaynaklarından biridir.
Edge Cases
Minimum, maksimum, boş değer ve özel karakter durumları edge case testlerine örnektir. Date boundary, leap year veya timezone gibi durumlar domain'e bağlı olarak önemli olabilir. Unit test hızlı olduğu için bu varyasyonları geniş biçimde çalıştırmak kolaydır. Property-based testing beklenmeyen kombinasyonlar bulmada ek avantaj sağlar. Edge case seçimi geçmiş bug'lardan da beslenmelidir.
Unit Test'te Neler Test Edilmemelidir?
Unit test sayısını artırmak amacıyla framework behavior veya trivial getter gibi düşük değerli alanları test etmek bakım maliyetini büyütebilir. Testin bize yeni bir güven sağlaması gerekir. ORM'nin kendi SQL üretim mekanizmasını mock üzerinden doğrulamak production query davranışını göstermez. Private method'ların doğrudan test edilmesi implementation'a aşırı bağlanma yaratabilir. Test stratejisinde kapsam kadar neyi test etmemeyi seçtiğimiz de önemlidir.
Framework'ün Kendisi
Framework routing veya validation mekanizmasının internals'ını unit test etmek genellikle gereksizdir. Kendi configuration'ınızın doğru uygulandığını integration seviyesinde doğrulamak daha değerlidir. Örneğin annotation gerçekten endpoint'te validation çalıştırıyor mu sorusu HTTP integration test ile cevaplanabilir. Framework kütüphanesinin kendi davranışı o kütüphanenin test sorumluluğudur. Kendi risk alanınıza odaklanmak suite'i daha anlamlı tutar.
ORM'nin Kendisi
ORM'nin query builder mantığını mocklayarak doğrulamak gerçek database davranışı hakkında yeterli güven sağlamaz. Repository query'sinin doğru sonucu üretmesi production engine üzerinde test edilmelidir. Mapping ve constraint davranışı da ancak gerçek database ile görülebilir. Unit testte ORM mock interaction sayısını kontrol etmek implementation detayına bağlanır. Repository'yi integration boundary olarak görmek çoğu projede daha sağlıklıdır.
Private Method'ların Doğrudan Davranışı
Private method implementation detail'dir ve public behavior üzerinden dolaylı test edilmelidir. Bir private method ayrıntılı test gerektiriyorsa ayrı domain abstraction'a dönüşmesi düşünülebilir. Reflection kullanarak private method test etmek refactor maliyetini artırır. Method ismi veya decomposition değiştiğinde business behavior aynı olsa bile test kırılabilir. Bu nedenle observable outcome'a odaklanmak daha güçlüdür.
Trivial Getter/Setter
Basit getter veya setter'ın platform dilinin doğal davranışını test etmek düşük değer üretir. Test sayısını yükseltir fakat production riskini anlamlı biçimde azaltmaz. Eğer setter validation veya side effect içeriyorsa durum değişir. Kural içeren davranış test edilmelidir. Coverage metriğini yükseltmek uğruna trivial kodu test etmek yerine riskli alanlara odaklanmak gerekir.
Implementation Detail
Hangi helper method çağrıldı veya internal collection kaç kez kullanıldı gibi bilgiler çoğu zaman dış davranış için önemli değildir. Bu ayrıntıları assert etmek refactor sırasında gereksiz kırılma yaratır. Test business input ve output ilişkisini doğrulamalıdır. Interaction verification yalnızca dependency interaction'ın iş sonucunun kendisi olduğu yerlerde kullanılmalıdır. Böylece kod yapısı değişse bile test değeri korunur.
Başka Library'nin İç Davranışı
Third-party library'nin kendi algoritmasını tekrar test etmek projenizin sorumluluğu değildir. Bunun yerine library ile sizin entegrasyonunuzun doğru configuration kullandığını doğrulayın. Library boundary kritikse contract veya integration test eklenebilir. Version upgrade sırasında smoke test davranış değişikliğini yakalayabilir. Kendi codebase'inizde kontrol ettiğiniz kararları test etmeye öncelik verin.
İyi Bir Unit Test Nasıl Yazılır?
İyi unit test okunabilir ve tek bir davranışı açık biçimde anlatır. Arrange aşamasında gerekli state kurulur, Act aşamasında davranış çalıştırılır ve Assert aşamasında observable outcome doğrulanır. Test setup ne kadar sade olursa failure nedeni o kadar hızlı anlaşılır. Test isimleri business senaryosunu anlatmalıdır. Benim deneyimimde okunabilir testlerin ekip içinde daha fazla güven oluşturduğu ve daha uzun ömürlü olduğu görülür.
Arrange
Arrange test için gereken input ve dependency durumunu hazırlar. Gereksiz fixture data oluşturmak testi okumayı zorlaştırır. Yalnızca davranış için önemli değerler açık biçimde görünmelidir. Test data factory valid default object üretmek için kullanılabilir. Override edilen alanlar test senaryosunun neyi değiştirdiğini göstermelidir.
Act
Act bölümü mümkünse tek anlamlı operation içermelidir. Bir test içinde birden fazla business action yapmak hangi adımın sonucu bozduğunu anlamayı zorlaştırır. Setup operation'ları Arrange bölümüne ait olmalıdır. Act sonucu exception veya return value olabilir. Davranışın gerçek giriş noktasını çağırmak implementation detail'e bağımlılığı azaltır.
Assert
Assert beklenen observable sonucu doğrular. Gereksiz her field'ı kontrol etmek testin bakım maliyetini artırabilir. Business açısından önemli alanlar seçilmelidir. Bir davranışın birden fazla ilgili sonucu varsa aynı test içinde birkaç assertion mantıklı olabilir. Assertion mesajları failure teşhisini kolaylaştırmalıdır.
Tek Bir Davranışa Odaklanmak
Tek davranış yaklaşımı “tek assertion” kuralı değildir. Örneğin başarılı sipariş davranışında status ve total birlikte anlamlı sonuç olabilir. Ancak aynı testte hem başarılı sipariş hem invalid payment senaryosu bulunmamalıdır. Test adı bir cümleyle neyi doğruladığını açıklayabilmelidir. Bu sınır failure localization açısından önemlidir.
Açık Test İsimleri
İyi test adı input durumunu ve beklenen sonucu anlaşılır biçimde ifade eder. Sadece Test1 veya CalculateTest gibi isimler yeterli bilgi vermez. Failure log test adıyla birlikte okunduğunda problem anlaşılabilmelidir. Business terminology kullanmak product ve engineering arasında ortak dil oluşturur. İsim standardı ekip içinde tutarlı uygulanmalıdır.
Test Başarısız Olduğunda Nedeni Anlaşılır Hale Getirmek
Test failure yalnızca “expected true but was false” mesajına düşmemelidir. Anlamlı assertion ve test adı hangi davranışın bozulduğunu göstermelidir. Büyük fixture veya onlarca mock interaction failure'ı asıl sorunu gizleyebilir. Test helper aşırı abstraction oluşturursa debugging zorlaşır. Okunabilir setup ve doğrudan assertion test bakımını ciddi biçimde kolaylaştırır.
Given–When–Then Yaklaşımı
Given, When ve Then yapısı testleri business senaryosu gibi okumayı kolaylaştırır. Given başlangıç koşullarını, When çalıştırılan davranışı, Then beklenen sonucu temsil eder. Bu model özellikle behavior-driven isimlendirmeyle iyi çalışır. Teknik olarak Arrange, Act ve Assert yapısıyla büyük ölçüde aynı fikri ifade eder. Hangi formatın seçildiğinden çok testin doğal ve tutarlı okunması önemlidir.
Given
Given test öncesindeki anlamlı durumu tanımlar. Örneğin “kullanıcının aktif aboneliği yok” bir Given durumudur. Gereksiz implementation setup'ları burada gizlenebilir fakat kritik business koşulu görünür kalmalıdır. Fixture helper bu hazırlığı sadeleştirebilir. Okuyan kişi davranışın hangi şartta çalıştığını hemen anlamalıdır.
When
When sistem üzerinde gerçekleştirilen olayı ifade eder. “Kullanıcı premium rapora erişmeye çalıştığında” gibi business dili kullanılabilir. Tek bir ana action tercih edilir. Birden fazla önemli action varsa senaryo iki teste bölünebilir. When testin gerçek behavior giriş noktasını göstermelidir.
Then
Then observable sonucu açıklar. Örneğin erişimin reddedilmesi ve uygun domain error dönmesi beklenebilir. Internal method call yerine kullanıcı veya caller tarafından görülen outcome tercih edilir. Side effect davranışın önemli parçasıysa ayrıca doğrulanabilir. Then bölümü testin neden var olduğunu açıklar.
AAA ile Arasındaki İlişki
Given çoğunlukla Arrange, When Act ve Then Assert ile eşleşir. İki yaklaşım teknik olarak rakip değildir. BDD terminolojisi business senaryosu okumayı kolaylaştırırken AAA daha teknik ve sade bir yapı sunar. Ekip tek standardı zorunlu tutmak yerine tutarlılığı hedefleyebilir. Testin anlaşılır olması isim seçiminin önündedir.
Behavior-Driven Test İsimlendirmesi
Behavior bazlı isimler class veya method isminden çok business sonucu anlatır. ShouldRejectOrder_WhenStockIsInsufficient buna örnek olabilir. Refactor method ismini değiştirse bile behavior aynı kaldığı için test adı geçerliliğini korur. Product requirement ile test arasındaki bağ güçlenir. Büyük test suite'lerde bu yaklaşım failure loglarını daha anlamlı yapar.
Unit Test İsimlendirme Stratejileri
Test isimlendirmesi teknik bir ayrıntı gibi görünse de büyük codebase'lerde ciddi fark yaratır. İsim testin hangi koşulu ve hangi sonucu doğruladığını anlatmalıdır. Method adı merkezli formatlar bazı ekiplerde kullanışlı olabilirken behavior bazlı format refactor sırasında daha dayanıklıdır. Tek doğru stil bulunmaz. Önemli olan failure listesine bakıldığında problemi test kodunu açmadan büyük ölçüde anlayabilmektir.
Method_Scenario_ExpectedResult
Bu format test edilen method, scenario ve sonucu birlikte gösterir. Küçük service class'larında anlaşılır olabilir. Method rename olduğunda test isimleri de değişmek zorunda kalabilir. Domain davranışı güçlü projelerde method yerine behavior ismi daha uygun olabilir. Yine de ekip standardı olarak tutarlı kullanıldığında faydalı bir yapı sağlar.
Should_When
Should_ReturnError_When_UserIsInactive gibi isimler beklenen davranışı önce gösterir. Failure log içinde sonucu hızlı okumayı kolaylaştırır. Method veya class internals'a daha az bağımlıdır. Business terminology ile birleştiğinde güçlü okunabilirlik sağlar. Test isimlerinin aşırı uzun olmaması için gereksiz ayrıntı eklenmemelidir.
Behavior Bazlı İsimlendirme
Behavior bazlı isim testin neden var olduğunu anlatır. Örneğin “stok yetersizken sipariş onaylanamaz” doğrudan business kuralıdır. Test edilen internal method değişse bile isim aynı kalabilir. Bu yaklaşım requirement ve test arasında güçlü izlenebilirlik oluşturur. Özellikle domain-heavy backend projelerinde çok kullanışlıdır.
Test Adından Failure Nedeni Anlaşılmalı mı?
Evet, ideal olarak failure listesi hangi davranışın bozulduğunu göstermelidir. Test adı tüm debugging bilgisini taşımak zorunda değildir. Ancak TestCalculate gibi genel isimlerden kaçınılmalıdır. Scenario ve expected outcome mümkün olduğunca isimde bulunmalıdır. Bu yaklaşım CI failure triage süresini azaltır.
Test Double Nedir?
Test double gerçek dependency yerine kontrollü davranış sağlayan test nesnesidir. Dummy, stub, spy, mock ve fake farklı amaçlarla kullanılır. Bu terimleri yalnızca teorik sınıflandırma olarak değil hangi riski kontrol ettiğimize göre değerlendirmek daha faydalıdır. Bir dependency yavaş, nondeterministic veya external ise test double değer sağlar. Production behavior'ın önemli bir kısmını saklıyorsa gerçek integration dependency tercih edilmelidir.
Dummy
Dummy yalnızca method signature gerektirdiği için verilen fakat testte gerçek davranışı kullanılmayan nesnedir. Örneğin çağrılmayacak bir logger dependency için dummy verilebilir. Testin ana davranışında rolü yoktur. Dummy üzerinde interaction assertion yapılmaz. Amaç yalnızca test setup'ını tamamlamaktır.
Stub
Stub testin ihtiyaç duyduğu kontrollü input'u üretir. Örneğin exchange rate client belirli oran döndürecek biçimde stub edilebilir. Test çoğunlukla stub'ın çağrılıp çağrılmadığını değil sistem sonucunu assert eder. State verification ile iyi uyum sağlar. Harici dependency response'larını kontrol etmek için çok kullanışlıdır.
Spy
Spy gerçek veya basit davranışın yanında çağrı bilgilerini kaydeder. Test sonrasında hangi argument ile çağrıldığı incelenebilir. Notification gönderimi gibi interaction'ın iş açısından önemli olduğu durumlarda faydalı olabilir. Her internal call için spy kullanmak implementation'a bağlanmayı artırır. Bu nedenle yalnızca observable side effect sınırlarında tercih edilmelidir.
Mock
Mock belirli interaction beklentilerini doğrulamak için kullanılır. Örneğin başarılı ödeme sonrasında notification gateway'in bir kez çağrılması beklenebilir. Ancak her dependency call'unu mock ile doğrulamak test suite'i kırılgan hale getirir. Refactor aynı sonucu üretse bile call order değiştiğinde test gereksiz kırılabilir. Mocking boundary interaction için ölçülü kullanılmalıdır.
Fake
Fake gerçek dependency'nin daha basit çalışan implementasyonudur. In-memory repository veya fake mail server buna örnek olabilir. Hızlı test sağlar fakat production semantics'i tam temsil etmeyebilir. Özellikle database fake'leri constraint, transaction ve SQL davranışını saklayabilir. Fake'in hangi farkları gizlediği açıkça bilinmelidir.
Hangisi Ne Zaman Kullanılmalı?
Kontrollü input gerekiyorsa stub çoğu zaman yeterlidir. Interaction iş sonucu açısından önemliyse mock veya spy düşünülebilir. Test setup için anlamsız dependency dummy olabilir. Production davranışına yakın basit implementation gerekiyorsa fake kullanılabilir. Seçim framework alışkanlığına değil testin doğruladığı riske göre yapılmalıdır.
Mock ve Stub Arasındaki Fark
Mock ve stub çoğu ekipte aynı kelime gibi kullanılsa da pratikte farklı test amaçlarını temsil eder. Stub sisteme kontrollü veri verirken mock genellikle bir interaction beklentisini doğrular. Stub kullanıldığında asıl assertion çoğunlukla sistem state veya return value üzerindedir. Mock kullanıldığında belirli method'un belirli sayıda çağrılması testin parçası olabilir. Interaction verification ihtiyaç olmadığı halde kullanılırsa test implementation detail'e gereksiz bağlanır.
State Verification
State verification işlem sonrasında ortaya çıkan sonucu kontrol eder. Örneğin order status veya hesaplanan total assert edilir. Stub dependency yalnızca gerekli input'u sağlar. Test internal call sequence ile ilgilenmez. Refactor toleransı genellikle yüksektir.
Behavior Verification
Behavior verification dependency interaction'ını doğrular. Örneğin payment başarılı olduğunda receipt sender'ın çağrılması gerekir. Buradaki interaction dışarıdan görülen side effect olduğu için anlamlıdır. Ancak repository method'unun tam olarak kaç kez çağrıldığını doğrulamak çoğu durumda düşük değerli olabilir. Her interaction'ın business açısından neden önemli olduğu sorulmalıdır.
Stub ile Controlled Input
Stub test scenario için belirli response üretir. Clock'ın sabit tarih döndürmesi buna iyi örnektir. Sistem bu input'a göre davranış üretir. Assert stub'ın kendisinde değil sonuçta yapılır. Bu model testleri okunabilir ve refactor'a dayanıklı tutar.
Mock ile Interaction Verification
Mock belirli dependency interaction beklentisi tanımlar. E-mail gateway'in hiç çağrılmaması veya bir kez çağrılması anlamlı bir contract olabilir. Argument'lar da doğrulanabilir. Interaction implementation detail değil observable side effect olmalıdır. Aksi halde mock sayısı büyüdükçe suite daha kırılgan hale gelir.
Aşırı Interaction Verification Riski
Her service çağrısının order ve count bilgisini assert etmek testin code structure kopyasına dönüşmesine yol açabilir. Refactor sırasında production behavior aynı kaldığı halde birçok test kırılır. Developer testleri düzeltmek için business risk yerine mock setup ile uğraşır. Bu durum testlere olan güveni azaltabilir. Interaction yalnızca davranışın anlamlı bir parçasıysa doğrulanmalıdır.
Mocking Ne Zaman Kullanılmalı?
Mocking özellikle external ve nondeterministic boundary'leri unit testten ayırmak için değerlidir. Network, zaman veya random dependency gerçek haliyle kullanıldığında test hızı ve determinism bozulabilir. Payment veya notification gateway gibi dış servisler test double ile controlled response verebilir. Bununla birlikte gerçek integration davranışı ayrı test seviyesinde korunmalıdır. Mock kullanımının amacı tüm sistemi sahte hale getirmek değil unit test sınırını güvenilir tutmaktır.
External API Client
External API client unit test içinde network çağrısı yapmamalıdır. Stub client success ve failure response'larını hızlı biçimde sağlayabilir. Domain veya service logic bu response'lara göre test edilir. Gerçek serialization ve HTTP davranışı ayrı integration testte doğrulanır. Böylece unit test hızlı kalırken boundary güveni de korunur.
Clock
Sistem saatini doğrudan kullanmak testleri tarihe bağımlı hale getirebilir. Clock abstraction ile fixed time enjekte edilebilir. Expiration ve scheduling logic deterministic olur. Production implementation gerçek zamanı döndürür. Test implementation ise açık bir timestamp kullanır.
Random Generator
Random değerler test sonucunu tahmin etmeyi zorlaştırabilir. Random generator abstraction ile belirli sequence üretilebilir. Retry jitter veya seçim algoritması böylece deterministically test edilir. Production'da gerçek randomness korunur. Test yalnızca kendi business davranışını kontrol eder.
E-mail/SMS Gateway
E-mail veya SMS göndermek unit testte gerçek provider çağrısı gerektirmez. Mock veya spy gönderim isteğinin oluştuğunu doğrulayabilir. Mesaj içeriği business açısından önemli alanlarla kontrol edilir. Provider API contract'ı ayrı integration veya contract testle korunur. Testler yanlışlıkla gerçek müşteriye mesaj göndermemelidir.
Payment Gateway Boundary
Payment gateway ücretli ve dış sistem olduğu için unit testte stub response kullanmak mantıklıdır. Success, decline ve timeout gibi senaryolar hızlı biçimde çalıştırılabilir. Payment request mapping ise local stub server ile integration seviyesinde doğrulanabilir. Duplicate payment riskine karşı idempotency ayrıca test edilmelidir. Gerçek production provider'a sürekli test request göndermek bağımlılığı artırır.
Yavaş veya Nondeterministic Dependency
Network ve sistem saati test suite'i yavaş veya flaky yapabilir. Unit sınırında bu dependency kontrol altına alınmalıdır. Ancak dependency'nin gerçek semantics'i kritikse integration suite mutlaka bulunmalıdır. Stub yalnızca logic isolation için kullanılır. Böylece hızlı test ve production fidelity arasında dengeli yapı kurulur.
Mocking Ne Zaman Kullanılmamalı?
Her internal class veya repository'yi mocklamak güçlü isolation sağlıyor gibi görünse de production davranışıyla test davranışı arasındaki mesafeyi büyütebilir. Özellikle ORM query ve database constraint'leri mock ile gerçekçi biçimde doğrulanamaz. Kendi domain object'leriniz hızlı ve deterministic ise onları gerçek haliyle kullanmak daha değerlidir. Mock test için gerekli bir araçtır, varsayılan architecture değildir. Boundary ve business davranışı ayrımını doğru yapmak önemlidir.
Kendi Domain Object'leriniz
Domain entity ve value object'ler hızlı ve local çalışıyorsa mocklanmaları çoğu zaman gerekmez. Gerçek object interaction business behavior'ın önemli parçasıdır. Mock domain model implementation detail'ini test içine taşır. Refactor sırasında gereksiz kırılma yaratabilir. Sociable unit test yaklaşımı burada daha okunabilir olabilir.
Her Internal Class
Bir service içindeki her collaborator'ı mocklamak test setup'ını uzun hale getirir. Test business sonucundan çok internal call graph'i doğrulamaya başlayabilir. Internal class'lar hızlıysa birlikte kullanmak daha faydalıdır. Boundary seviyesinde abstraction yapmak yeterli olabilir. Test double sayısı arttıkça production behavior ile test behavior arasındaki fark da artar.
ORM Query Davranışı
ORM query'nin doğru SQL üretip doğru kayıtları döndürmesi gerçek database ile doğrulanmalıdır. Mock repository yalnızca sizin yazdığınız mock response'u geri verir. Complex filter veya join hataları bu testte görünmez. Database integration test burada daha yüksek güven sağlar. ORM'nin kendisini değil sizin query mapping'inizi test etmiş olursunuz.
Database Semantiği
Transaction, unique constraint, foreign key ve locking davranışı fake dependency ile tam temsil edilemez. Production database'in gerçek engine'i testte kullanılmalıdır. Testcontainers bu ihtiyacı local ve CI ortamında kolaylaştırabilir. Concurrent write veya migration hataları ancak gerçek semantics ile görülebilir. Bu nedenle database boundary mocklamanın sınırı açık olmalıdır.
Mock'un Production Davranışını Taklit Edemediği Durumlar
Mock yalnızca bizim tanımladığımız davranışı gösterir. Gerçek dependency beklenmeyen format, timeout veya constraint davranışı üretebilir. Test suite tamamen mock'lara dayanıyorsa tüm testler yeşilken production hata verebilir. Bu durum integration test eksikliğinin güçlü işaretidir. Mock coverage ile gerçek sistem güveni birbirine karıştırılmamalıdır.
Mock-Heavy Test Suite Problemi
Mock-heavy suite genellikle çok sayıda unit test içerir fakat component boundary hatalarını yeterince yakalayamaz. Her internal interaction test edildiği için küçük refactor'lar büyük test kırılmalarına yol açabilir. Daha kötüsü, mock contract yanlış tanımlandığında test production dependency'nin gerçek davranışından habersiz kalır. Bu nedenle yeşil test sayısı güvenli production anlamına gelmeyebilir. Behavior testleri ve gerçek integration boundary testleri daha dengeli bir yapı oluşturur.
Implementation'a Aşırı Bağlanma
Test internal method call sırasını takip ettiğinde kod yapısının kopyasına dönüşür. Bir algorithm aynı output'u daha farklı collaborator sequence ile ürettiğinde test gereksiz kırılır. Developer refactor yapmaktan kaçınabilir. Test business sonucu yerine implementation'ı korur. Bu durum uzun vadede codebase esnekliğini azaltır.
Refactor Sırasında Gereksiz Test Kırılması
İyi refactor observable behavior'ı değiştirmeden implementation'ı iyileştirir. Behavior testleri çoğunlukla geçmeye devam eder. Mock-heavy testler ise internal dependency değişikliğinde fail olabilir. Çok sayıda testi business change olmadan güncellemek zorunda kalırsınız. Bu maliyet ekiplerin testleri yük olarak görmesine yol açabilir.
Yeşil Testlere Rağmen Production Hatası
Repository mock doğru kayıt döndürür fakat gerçek SQL yanlış olabilir. External API mock beklenen JSON verirken provider gerçek response'ta farklı alan döndürebilir. Bu durumda unit testlerin tamamı geçebilir. Integration ve contract test eksikliği production riskini büyütür. Test portföyü gerçek boundary'lerde de güven üretmelidir.
Interaction Testleri Yerine Behavior Testleri
Test mümkün olduğunca observable result doğrulamalıdır. Interaction yalnızca e-mail gönderme gibi business side effect'in kendisi olduğunda assert edilmelidir. Internal repository çağrı count'ları çoğu zaman asıl sonuç değildir. Behavior odaklı testler refactor'a daha toleranslıdır. Bu yaklaşım suite'in uzun vadeli değerini artırır.
Dependency Injection Test Edilebilirliği Nasıl Artırır?
Dependency injection kodun dış dependency'lerini açık hale getirir ve test sırasında kontrollü implementation kullanmayı kolaylaştırır. Constructor üzerinden verilen clock, API client veya gateway test double ile değiştirilebilir. Hard-coded singleton veya doğrudan network client oluşturmak test sınırını zorlaştırır. Interface kullanımı her durumda zorunlu değildir, ancak gerçek boundary için faydalı olabilir. Test edilebilirlik iyi dependency tasarımının doğal sonucudur.
Dependency'leri Constructor Üzerinden Almak
Constructor injection object'in çalışması için hangi dependency'lere ihtiyaç duyduğunu açıkça gösterir. Test setup sırasında stub veya fake verilebilir. Runtime configuration production implementation'ı sağlar. Hidden global dependency azalır. Bu yapı hem test hem bakım açısından daha anlaşılırdır.
Interface Kullanımı
Interface dış servis veya altyapı boundary'sini soyutlamak için değerlidir. Her class için interface oluşturmak gerekli değildir. Test double ihtiyacı gerçek abstraction gereksinimiyle birlikte değerlendirilmelidir. Domain object'ler çoğunlukla concrete kalabilir. Boundary interface business code'u infrastructure detayından ayırır.
Test Double Enjekte Etmek
Test sırasında dependency'nin controlled implementation'ı constructor üzerinden verilir. Stub belirli response döndürür veya spy interaction kaydeder. Test production configuration'a ihtiyaç duymaz. Behavior deterministic hale gelir. Integration testte ise aynı interface'in gerçek implementation'ı kullanılabilir.
Hard-Coded Dependency Anti-Pattern
Service içinde doğrudan HTTP client veya system clock oluşturmak test kontrolünü azaltır. Global static state paralel testlerde sorun çıkarabilir. Dependency açık olmadığında hangi external etkilerin bulunduğu anlaşılmaz. Constructor veya function parameter tercih etmek daha sade bir tasarım sunar. Dependency injection yalnızca framework özelliği değil tasarım prensibidir.
Time ve Randomness Nasıl Test Edilir?
Zaman ve randomness flaky testlerin önemli kaynaklarıdır. Kod doğrudan system clock veya random generator kullandığında test sonucu çalıştırıldığı ana göre değişebilir. Bu dependency'leri abstraction üzerinden geçirmek controlled input sağlar. Fixed time ve deterministic UUID üretimi test senaryolarını tekrar edilebilir hale getirir. Production implementation gerçek davranışı korurken test implementation açık ve tahmin edilebilir değerler üretir.
System Clock'a Doğrudan Bağımlılık Problemi
now() doğrudan birçok yerde çağrıldığında test tam zamanı kontrol edemez. Expiration boundary milisaniye farkıyla flaky olabilir. Timezone veya daylight saving farklılıkları da sorun yaratabilir. Clock service merkezi zaman kaynağı sağlar. Test fixed instant kullanır.
Clock Abstraction
Clock abstraction current time sağlayan küçük bir dependency olabilir. Production clock gerçek sistem zamanını döndürür. Test clock belirli zaman verir ve gerekirse ileri alınabilir. Scheduling ve expiration logic daha kolay test edilir. Bu yaklaşım test readability açısından da faydalıdır.
Fixed Time
Test içinde açık timestamp kullanmak scenario'yu anlaşılır hale getirir. “2026-08-24 saat 10:00” gibi sabit değerle expiration öncesi ve sonrası ayrı test edilir. Test gün veya saat değişiminden etkilenmez. CI timezone farkı sorun olmaktan çıkar. Boundary case'ler kontrollü biçimde çalıştırılır.
Random Generator Injection
Random selection veya retry jitter kullanan kod injection ile test edilebilir hale gelir. Test generator belirli sequence döndürür. Böylece beklenen branch güvenilir biçimde çalıştırılır. Production generator gerçek random değer üretir. Test randomness'in kendisini değil sizin logic'inizi doğrular.
Deterministic UUID
UUID üretimi çoğu sistemde random olabilir. Test içinde sabit UUID generator kullanmak expected object ve event değerlerini kolaylaştırır. Test logları daha anlaşılır hale gelir. Aynı resource ID ile idempotency davranışı kontrollü biçimde çalıştırılabilir. Production uniqueness davranışı ayrı integration veya library güvenine bırakılabilir.
Parameterized Test Nedir?
Parameterized test aynı davranışı farklı input değerleriyle tekrar çalıştırmayı sağlar. Validation, boundary ve calculation senaryolarında duplicate test kodunu azaltır. Her case hangi input ve beklenen sonucu temsil ettiğini açıkça göstermelidir. Çok fazla farklı behavior tek parameterized test içinde birleştirilmemelidir. Aynı kuralın data varyasyonları için güçlü ve okunabilir bir yöntemdir.
Aynı Davranışı Birden Fazla Input ile Test Etmek
Örneğin yaş sınırı kuralı birçok değerle test edilebilir. Test structure bir kez yazılır ve input tablosu ayrı tutulur. Hangi case başarısız oldu test runner tarafından görünür olmalıdır. Parameter list business anlamını kaybetmeyecek kadar sade olmalıdır. Karmaşık senaryolar ayrı test isimleriyle daha okunabilir olabilir.
Boundary Values
Minimum ve maksimum limitlerin tam sınır değerleri özellikle test edilmelidir. Bir altı ve bir üstü off-by-one hatalarını yakalar. Business limit değiştiğinde parameter table kolay güncellenir. Integration seviyesinde tüm boundary permütasyonunu tekrar etmek gerekmez. Unit seviyesinde ucuz biçimde geniş coverage sağlanır.
Invalid Inputs
Null, empty veya geçersiz formatlar parameterized validation testine uygundur. Her input için beklenen error code veya message doğrulanabilir. Framework serialization hataları ayrıca API integration testte ele alınmalıdır. Domain validation ile transport validation ayrılmalıdır. Bu ayrım test duplication'ı azaltır.
Table-Driven Tests
Table-driven test input ve expected result çiftlerini okunabilir tabloda tutar. Go gibi dillerde yaygın olsa da birçok framework'te benzer yaklaşım bulunur. Business rule varyasyonlarını tek yerde görmek kolaydır. Her row bağımsız scenario olmalıdır. Failure output ilgili case'i açık biçimde göstermelidir.
Boundary Value Testing
Boundary value testing çoğu bug'ın koşul sınırlarında ortaya çıktığı varsayımından yararlanır. Minimum, maksimum ve bu değerlerin hemen dışındaki input'lar özellikle önemlidir. Empty, null veya Unicode gibi değerler de backend validation ve serialization sorunlarını ortaya çıkarabilir. Bu testlerin çoğu unit seviyesinde çok hızlı çalışır. API boundary'sinde yalnızca transport behavior için gerekli örnekleri tekrar etmek yeterlidir.
Minimum
Minimum geçerli değer doğrudan test edilmelidir. Bir limit 1 ise 1 değerinin kabul edildiği doğrulanır. Bu test inclusive veya exclusive sınır belirsizliğini giderir. Requirement değişirse test davranış değişikliğini görünür kılar. Test adı sınırı açıkça belirtmelidir.
Maximum
Maksimum geçerli değer kabul edilmelidir. Büyük sayılar integer overflow veya rounding sorunlarını da gösterebilir. Business maksimumu ile teknik type maksimumu birbirinden ayrılmalıdır. Her ikisi farklı risk taşır. Test requirement seviyesindeki gerçek kuralı doğrulamalıdır.
Minimum - 1
Minimumun hemen altındaki değer reddedilmelidir. Bu case off-by-one hatasını yakalamak için önemlidir. Error tipi veya validation result doğrulanabilir. Transport layer'da aynı case'i yalnızca gerekli olduğunda tekrar test edin. Unit test kuralı en hızlı biçimde doğrular.
Maximum + 1
Maksimumun hemen üstündeki değer geçersiz olmalıdır. Limit enforcement burada net biçimde görülür. Type overflow varsa test input üretiminde dikkat gerekir. Domain limit ile storage limit ayrı test edilebilir. Boundary kuralları documentation ile uyumlu olmalıdır.
Empty
Empty string veya collection bazı alanlarda geçersiz, bazı alanlarda anlamlı olabilir. Requirement açık biçimde test edilmelidir. Whitespace-only input ayrıca değerlendirilebilir. Serialization empty değeri null'dan farklı ele alabilir. API ve domain validation sınırı doğru yerde olmalıdır.
Null
Null davranışı language ve framework'e göre değişebilir. Domain model null kabul etmiyorsa constructor veya validator bunu açık biçimde reddetmelidir. HTTP payload'da missing field ve explicit null farklı davranabilir. Bu fark integration testte görülebilir. Unit test business null kuralını korur.
Unicode ve Özel Karakterler
Kullanıcı adı, açıklama veya arama alanlarında Unicode input gerçek kullanım senaryosudur. Encoding ve normalization farkları database veya API seviyesinde sorun çıkarabilir. Domain validation özel karakterleri gereksiz yere reddetmemelidir. Database collation davranışı integration testle doğrulanabilir. Security escaping ayrıca ilgili boundary testlerinde ele alınmalıdır.
Property-Based Testing
Property-based testing belirli birkaç örnek yerine çok sayıda input üreterek her durumda korunması gereken özelliği test eder. Örneğin bir normalize işleminin iki kez uygulanmasının aynı sonucu vermesi bir property olabilir. Framework random input üretir ve failure bulduğunda daha küçük örneğe indirgemeye çalışır. Validation, parsing ve calculation gibi alanlarda beklenmeyen edge case'ler yakalayabilir. Example-based testlerin yerine geçmekten çok onları tamamlar.
Example-Based Test ile Farkı
Example-based test belirli input ve sonucu tanımlar. Property test ise birçok input için geçerli genel kuralı belirtir. İkisi birlikte güçlü coverage sağlar. Kritik business örnekleri yine açık test case olarak tutulabilir. Property-based test bilmediğimiz edge case'leri arar.
Property Tanımlamak
Property sistemin her geçerli input için koruması gereken invariant'tır. Örneğin toplamın negatif olmaması veya encode-decode sonucunun orijinal değeri vermesi olabilir. Property business anlamı taşımalıdır. “Exception atmıyor” tek başına zayıf property olabilir. Net invariant iyi test üretmenin temelidir.
Random Input Generation
Framework birçok kombinasyon üretir. Input generator domain kurallarına göre özelleştirilebilir. Tamamen anlamsız data üretmek test değerini düşürebilir. Valid ve invalid generator'lar ayrı tasarlanabilir. Failure seed saklanırsa aynı case tekrar üretilebilir.
Shrinking
Shrinking failure yaratan karmaşık input'u daha küçük ve anlaşılır hale getirir. Örneğin uzun string yerine problemi üreten tek karakter bulunabilir. Debugging süresini ciddi biçimde azaltır. Framework desteği kullanılan dile göre değişebilir. Minimal failing example regression testine dönüştürülebilir.
Hangi Backend Problemlerinde Faydalıdır?
Parser, serializer, validation ve calculation property testing için iyi adaylardır. Idempotency ve state machine invariant'ları da test edilebilir. Security normalization veya input transformation alanlarında beklenmeyen kombinasyonlar bulunabilir. Database-heavy behavior için generated data integration testte kullanılabilir fakat runtime maliyeti artar. Risk ve test süresi birlikte değerlendirilmelidir.
Integration Test Nedir?
Integration test iki veya daha fazla component'in gerçek boundary üzerinden birlikte doğru çalıştığını doğrular. Backend uygulamada database, HTTP pipeline, queue, cache veya external service client tipik integration alanlarıdır. Unit testten daha yavaş olabilir fakat production davranışına daha fazla benzer. API veritabanı ve servis entegrasyon testleri nasıl yapılır sorusunun cevabı çoğu zaman gerçek dependency semantics'i mümkün olduğunca korumaktır. Integration testin amacı unit testte sahtelediğimiz sınırların gerçek dünyada beklediğimiz gibi çalıştığını göstermektir.
Unit Test ile Farkı
Unit test hızlı ve dar davranış sınırına odaklanır. Integration test component boundary'nin gerçek davranışını doğrular. Database query unit mock ile değil gerçek database ile daha güvenilir test edilir. Integration test setup ve runtime maliyeti daha yüksektir. Bu nedenle business permutation'ların tamamı integration seviyesinde tekrarlanmamalıdır.
Birden Fazla Component'in Birlikte Çalışmasını Test Etmek
Repository ile database veya controller ile middleware birlikte test edilebilir. Buradaki amaç component contract'ında oluşabilecek hataları bulmaktır. Serialization veya mapping hatası unit testlerin gözünden kaçabilir. Integration test gerçek data akışını kullanır. Test sınırı çok genişlerse failure localization zorlaşabileceği için kapsam bilinçli seçilmelidir.
Infrastructure Boundary'lerini Test Etmek
Database constraint, message acknowledgement ve cache TTL infrastructure semantics'e bağlıdır. Bunları fake implementation ile test etmek yanıltıcı olabilir. Gerçek dependency container kullanmak daha yüksek fidelity sağlar. Test setup otomatik ve disposable olduğunda isolation da korunur. Testcontainers yaklaşımı burada güçlü bir araçtır.
Integration Test'in Maliyeti
Integration test unit teste göre daha fazla startup ve cleanup maliyeti taşır. Container başlatma veya migration çalıştırma süreyi uzatabilir. Failure birden fazla component'ten kaynaklanabileceği için teşhis daha fazla log gerektirebilir. Buna karşılık production boundary hatalarını erken bulur. Test portföyünde maliyet ile güven dengesi kurulmalıdır.
Backend'de Hangi Noktalar Integration Test Edilmeli?
Integration test adaylarını gerçek uygulama sınırları üzerinden belirlemek güçlü bir yöntemdir. Database, REST API, message queue, cache, filesystem, object storage ve external HTTP client bu sınırların başında gelir. Serialization ve deserialization da iki component arasındaki contract'ı etkilediği için integration test konusu olabilir. Her sınır için gerçeğe yakın dependency kullanmak önemlidir. Bu yaklaşım test seviyelerini teorik sınıflar yerine production mimarisi üzerinden tanımlar.
Database
Repository query, ORM mapping, migration ve constraint gerçek database üzerinde test edilmelidir. PostgreSQL kullanılıyorsa testte aynı major version'a yakın PostgreSQL tercih edilebilir. Containerized database bu ortamı kolayca oluşturabilir. Data isolation her test için korunmalıdır. Query sonucu kadar database error davranışı da test edilmelidir.
REST API
REST integration test gerçek HTTP request pipeline kullanmalıdır. Routing, middleware, authentication, validation ve serialization birlikte doğrulanabilir. Test server production network'e çıkmadan application host'u çalıştırabilir. Database gerçek container olabilir. External third-party dependency gerektiğinde kontrollü stub ile değiştirilebilir.
Message Queue
Queue integration test gerçek broker ile publish ve consume davranışını doğrulayabilir. Serialization, routing key ve acknowledgement burada önemlidir. Dead letter configuration test edilebilir. Consumer idempotency ayrıca doğrulanmalıdır. Broker container suite ömrü boyunca paylaşılabilir.
Cache
Redis veya benzeri cache gerçek instance ile test edildiğinde TTL ve serialization davranışı görünür hale gelir. Fake dictionary production semantics'i temsil etmeyebilir. Cache hit, miss ve invalidation senaryoları ayrı test edilmelidir. Parallel tests için key namespace kullanılabilir. Cleanup her test sonrasında güvenli biçimde yapılmalıdır.
File System
File read-write behavior OS path ve permission semantics'e bağlı olabilir. Temporary directory kullanmak izolasyon sağlar. Filename normalization ve cleanup test edilmelidir. Production object storage kullanılıyorsa filesystem testi yalnızca local abstraction'ın davranışını doğrular. S3 client gibi boundary ayrıca integration test gerektirir.
Object Storage
Object storage testleri upload, download, metadata ve signed URL üretimini kapsayabilir. Emulator veya uyumlu local dependency kullanılabilir. Production provider'a her testte erişmek maliyet ve flakiness yaratabilir. Key naming ve error mapping doğrulanmalıdır. Security-sensitive access policy için ayrı environment testleri gerekebilir.
External Service Client
Client'ın request oluşturması ve response parse etmesi integration seviyesinde test edilmelidir. Local HTTP stub server gerçek network protocol davranışına yaklaşır. Timeout, 429 ve invalid JSON gibi senaryolar kontrollü üretilebilir. Third-party sandbox ek smoke test sağlayabilir fakat ana suite için tek kaynak olmamalıdır. Contract değişiklikleri ayrı testlerle korunabilir.
Serialization / Deserialization
DTO mapping ve JSON serialization production contract'ın önemli parçasıdır. Unit test yalnızca object üzerinde çalışıyorsa casing veya converter hatasını kaçırabilir. HTTP veya message integration test gerçek serializer kullanmalıdır. Unknown field veya null behavior test edilebilir. Version compatibility event sistemlerinde özellikle önemlidir.
Narrow ve Broad Integration Test Arasındaki Fark
Narrow integration test az sayıda component'i ve tek boundary'yi doğrular. Broad integration test daha fazla altyapı ve application katmanını birlikte çalıştırır. Narrow testler daha hızlı ve failure localization açısından daha kolaydır. Broad testler production'a daha yakın confidence sağlayabilir fakat runtime maliyeti yüksektir. İki yaklaşım birbirinin yerine geçmez ve risk seviyesine göre birlikte kullanılabilir.
Narrow Integration Test
Repository ile gerçek database testi narrow integration örneğidir. HTTP server veya diğer servisler çalışmayabilir. Test yalnızca SQL ve mapping boundary'sini doğrular. Setup hızlıdır ve failure nedeni çoğunlukla database layer'dadır. Çok sayıda repository senaryosu için uygundur.
Broad Integration Test
Gerçek HTTP request, application service ve database birlikte çalışabilir. Bazı dış servisler stub server ile değiştirilebilir. Routing ve transaction boundary birlikte doğrulanır. Unit seviyesinde gözden kaçan wiring sorunları yakalanabilir. Fakat test sayısı kontrollü tutulmalıdır.
Dependency Sayısı
Dependency sayısı arttıkça test fidelity artabilir fakat setup maliyeti de büyür. Her dependency'nin gerçek olması zorunlu değildir. Test edilen boundary açısından önemli olmayan dış servisler stub olabilir. Database gerçek kalırken e-mail gateway mocklanabilir. Bu hibrit yaklaşım test scope'unu anlaşılır tutar.
Execution Time
Narrow testler çoğunlukla daha hızlı çalışır. Broad test application boot ve birden fazla dependency startup gerektirebilir. Suite içinde runtime budget belirlemek faydalıdır. Merge request için hızlı testler öncelikli tutulabilir. Ağır senaryolar sonraki stage'e taşınabilir.
Failure Localization
Narrow test failure belirli boundary'yi işaret eder. Broad testte database, HTTP veya application configuration aynı anda aday olabilir. İyi logging ve test fixture bu problemi azaltır. Her behavior'ı yalnızca broad testte doğrulamak debugging maliyetini artırır. Test katmanları birbirini tamamlamalıdır.
Hangi Yaklaşım Ne Zaman Kullanılmalı?
Specific repository query için narrow test daha uygundur. Kritik endpoint'in gerçek pipeline ve database ile çalışması için broad integration değer sağlar. Her business edge case broad testte tekrarlanmamalıdır. Risk ve execution cost birlikte değerlendirilmelidir. Test portföyü sabit kurala değil mimariye göre şekillenir.
Database Integration Testleri
Database integration testleri backend uygulamalarında en yüksek değer sağlayan test alanlarından biridir. Repository, ORM mapping, SQL query, transaction ve constraint davranışları gerçek engine üzerinde doğrulanabilir. Fake repository hızlıdır fakat production database'in davranışını göstermediği için tek başına yeterli değildir. PostgreSQL gibi güçlü relational database kullanan projelerde gerçek container ile test yapmak çoğu zaman ciddi production hatalarını erkenden yakalar. Özellikle migration ve concurrency senaryoları unit testlerle güvenilir biçimde doğrulanamaz.
Repository Layer
Repository method'u gerçek database'e query göndermelidir. Test data hazırlanır, repository çağrılır ve sonuç doğrulanır. Filtre, sorting ve pagination behavior gerçek SQL ile değerlendirilir. Mock DbSet veya fake repository üretmek yerine database boundary test edilir. Test yalnızca ORM invocation değil business query sonucunu assert eder.
ORM Mapping
Entity-column mapping yanlışsa application runtime'da hata verebilir. Column type, nullable ve relationship configuration gerçek database ile doğrulanır. Migration schema ile ORM model arasındaki drift yakalanabilir. Complex value converter'lar da bu seviyede test edilebilir. Mapping testlerinin çoğu normal repository senaryoları içinde doğal olarak kapsanır.
SQL Query
Raw SQL veya generated SQL gerçek engine üzerinde çalıştırılmalıdır. Syntax ve function desteği engine'e özgüdür. PostgreSQL query'sini SQLite üzerinde başarılı görmek production doğruluğunu garanti etmez. Query sonucu ve edge data cases test edilmelidir. Performance-critical query için ayrı benchmark veya explain analizi gerekebilir.
Transaction
Multi-step write operation transaction boundary gerektiriyorsa rollback davranışı test edilmelidir. İkinci operation fail olduğunda ilk değişiklik kalmamalıdır. Gerçek transaction isolation seviyesinde concurrency testleri yapılabilir. Fake database transaction davranışını doğru yansıtmayabilir. Business atomicity production engine üzerinde doğrulanmalıdır.
Constraint
Unique, foreign key ve check constraint data integrity için güçlü savunmadır. Test duplicate veya invalid relation oluşturmaya çalışmalıdır. Uygulama database error'ını doğru domain error'a dönüştürebilir. Constraint migration sırasında gerçekten oluşmuş mu ayrıca doğrulanmalıdır. Sadece application validation'a güvenmek yeterli değildir.
Stored Procedure
Stored procedure veya database function kullanılıyorsa gerçek engine test zorunludur. Input, output ve transaction behavior doğrulanmalıdır. Schema migration procedure version'ını doğru kurmalıdır. Error handling application layer ile uyumlu olmalıdır. Farklı database engine kullanmak burada özellikle yanıltıcı olur.
Database-Specific Behavior
JSON operator, collation, timezone ve locking davranışı database'e özgü olabilir. Bu özellikler fake dependency ile temsil edilemez. Production engine container üzerinden test edilmelidir. Engine version upgrade sırasında regression suite önem kazanır. Database-specific feature kullanmak yanlış değildir, ancak test ortamı bunu gerçekten doğrulamalıdır.
Testte Gerçek Database mi In-Memory Database mi Kullanılmalı?
In-memory database hızlı setup avantajı sağlayabilir fakat production semantics'ten farklı davranıyorsa integration güveni düşer. Testin amacı repository ve SQL behavior'ını doğrulamaksa production ile aynı engine'e yakın dependency kullanmak daha sağlıklıdır. Modern container tabanlı test yaklaşımı bu maliyeti önemli ölçüde azaltır. In-memory çözüm yalnızca belirli hızlı testlerde uygun olabilir. Hangi farkları gizlediğini bilmeden production database yerine kullanmak risklidir.
In-Memory Database Avantajları
In-memory database genellikle hızlı başlar ve local dependency gerektirmez. Basit CRUD testlerinde kolay setup sunar. Ancak bu hız her test için gerekli olmayabilir. Integration testin gerçek semantics doğrulama amacıyla çelişebilir. Hız ihtiyacı gerçek database container reuse ile de yönetilebilir.
Production Database ile Davranış Farkları
Query planner, type conversion ve transaction behavior engine'ler arasında değişebilir. In-memory provider bazı unsupported query'leri farklı şekilde evaluate edebilir. Production'da fail edecek bir query testte geçebilir. Bu false confidence ciddi problemdir. Test ortamı production'a anlamlı ölçüde benzemelidir.
SQL Dialect Farkları
SQL standardı bulunsa da engine-specific syntax ve function'lar yaygındır. PostgreSQL JSON operator'ü veya array behavior başka engine'de aynı olmayabilir. ORM translation da provider'a göre değişebilir. Aynı application query farklı SQL üretir. Bu nedenle gerçek provider integration testleri önemlidir.
Constraint Farkları
Foreign key enforcement veya unique collation semantics farklı olabilir. Bazı in-memory provider'lar constraint'i tam uygulamayabilir. Application testleri geçerken production data corruption riski oluşabilir. Constraint testleri gerçek engine üzerinde yapılmalıdır. Migration sonucu oluşan constraint'ler ayrıca doğrulanabilir.
Transaction Farkları
Transaction isolation ve locking gerçek database davranışıdır. In-memory implementation bunları basitleştirebilir. Concurrent update veya deadlock senaryosu test edilemeyebilir. Critical transaction logic production engine ile doğrulanmalıdır. Bu özellikle finansal işlemlerde önemlidir.
Production'a Yakınlık
Test dependency ne kadar production'a benzerse integration confidence o kadar yükselir. Aynı engine ve benzer version kullanmak güçlü başlangıçtır. Aynı configuration'ın tümünü kopyalamak zorunlu değildir. Test isolation ve hız için disposable container kullanılabilir. Fidelity bilinçli bir trade-off olarak yönetilmelidir.
PostgreSQL Kullanırken Testte SQLite/H2 Kullanmanın Riskleri
Production PostgreSQL kullanırken integration testte SQLite veya H2 seçmek hızlı görünse de query ve constraint davranışında önemli farklar oluşturabilir. SQL syntax, data type, JSON, date/time ve collation semantics aynı değildir. ORM provider farklı SQL üretebilir. Özellikle database-specific feature kullanan projelerde testler yanlış güven sağlayabilir. PostgreSQL container kullanmak bu farkların büyük bölümünü ortadan kaldırır.
SQL Syntax
Function isimleri ve query syntax engine'e göre değişir. PostgreSQL'e özel operator veya CTE davranışı başka engine'de farklı olabilir. ORM bazı query'leri provider-specific translate eder. Test farklı provider kullanırsa production query hiç çalıştırılmamış olur. Bu nedenle integration testte gerçek provider önemlidir.
Data Types
UUID, array, enum veya numeric type behavior engine'ler arasında farklıdır. Type conversion error production'da ortaya çıkabilir. Fake provider daha esnek conversion uygulayabilir. Schema mapping testleri aynı engine'i kullanmalıdır. Özellikle precision gerektiren finansal alanlar dikkat ister.
JSON
PostgreSQL JSONB query ve index özellikleri güçlüdür. SQLite veya H2 aynı operator ve semantics'i sunmayabilir. ORM query testleri farklı SQL üretir. JSON path ve containment behavior gerçek database üzerinde doğrulanmalıdır. Database-specific index performance ayrıca başka test gerektirebilir.
Date/Time
Timestamp, timezone ve date arithmetic engine'e göre değişebilir. UTC normalization veya offset behavior testte farklı sonuç verebilir. Production bug'ları çoğu zaman timezone sınırlarında ortaya çıkar. Same-engine integration test bu riski azaltır. Domain logic yine fixed clock ile unit test edilebilir.
Collation
String sorting ve case sensitivity database collation'a bağlıdır. Aynı query farklı engine'de farklı sıralama üretebilir. Unique constraint de case sensitivity nedeniyle değişebilir. Search behavior production engine'de doğrulanmalıdır. Unicode input özellikle test edilmelidir.
Transaction Isolation
Isolation level ve locking semantics engine-specific'tir. PostgreSQL MVCC davranışı başka test database'iyle birebir aynı değildir. Concurrent request testleri bu nedenle production engine gerektirir. Lost update ve unique race senaryoları gerçek transaction behavior'a dayanır. Fake environment bu hataları saklayabilir.
Index ve Constraint Davranışı
Index type ve partial index gibi özellikler PostgreSQL'e özgü olabilir. Constraint error code'ları application mapping için önemli olabilir. Test database farklıysa error handling hiç doğrulanmamış olur. Migration'ların index'i doğru oluşturduğu da gerçek engine'de görülmelidir. Production'a yakınlık burada doğrudan güven üretir.
Testcontainers Nedir?
Testcontainers integration test sırasında ihtiyaç duyulan database, Redis, message broker veya başka dependency'leri disposable container olarak başlatmayı kolaylaştıran bir yaklaşımdır. Shared staging database yerine her test suite kendi izole dependency ortamını kullanabilir. Bu yapı local ve CI test davranışını birbirine yaklaştırır. Container test sonunda otomatik temizlenebilir. Backend test otomasyonu açısından en büyük avantajı, gerçek dependency semantics'i kullanılabilir maliyette test sürecine dahil etmesidir.
Disposable Infrastructure
Disposable infrastructure test için oluşturulur ve iş bitince kaldırılır. Uzun süre yaşayan shared environment state problemi azalır. Migration her suite başında temiz schema üzerine uygulanabilir. Test sonucu önceki developer'ın bıraktığı data'dan etkilenmez. Bu isolation flaky testleri azaltır.
Gerçek Database Container'ı
PostgreSQL veya MySQL gerçek image ile çalıştırılabilir. Application test connection string'i container'dan alır. Migration production'daki aynı tool ile uygulanır. Repository testleri gerçek engine semantics'i kullanır. CI runner yalnızca container runtime'a ihtiyaç duyar.
Gerçek Redis
Redis container cache TTL ve serialization testlerine production'a yakın ortam sağlar. Fake dictionary yerine gerçek command semantics kullanılır. Key cleanup ve expiration behavior görülebilir. Parallel suite için ayrı container veya namespace kullanılabilir. Cache integration confidence yükselir.
Gerçek Message Broker
RabbitMQ veya Kafka container publish-consume pipeline'ını test etmeyi kolaylaştırır. Topic, exchange ve queue configuration gerçekten uygulanır. Serialization ve acknowledgement davranışı görünür hale gelir. Eventual consistency için explicit waiting gerekir. Test broker production cluster'ın tamamını taklit etmek zorunda değildir.
Test Sonrası Otomatik Cleanup
Container test suite sona erdiğinde kaldırılabilir. Manual database reset ihtiyacı azalır. Failure sonrasında state temiz kalır. Debugging için bazı projeler local reuse seçeneği sunabilir. CI ortamında fresh container daha güvenilir isolation sağlar.
Local ve CI Ortamında Aynı Test Yapısı
Developer ve CI aynı container definition kullanabilir. “Bende çalışıyor” problemleri azalır. Dependency version image tag ile sabitlenebilir. Infrastructure setup README yerine executable test configuration içinde tutulur. Bu yaklaşım onboarding süresini de kısaltır.
Testcontainers ile Database Test Akışı
Testcontainers tabanlı database integration testinin akışı basit ve tekrar edilebilir olmalıdır. Önce container başlatılır, connection bilgisi alınır ve migration uygulanır. Ardından minimum test data hazırlanır ve repository veya API davranışı çalıştırılır. Assertion sonrasında test state temizlenir veya container suite sonunda kaldırılır. Bu standardizasyon hem local hem CI ortamında güvenilir backend test otomasyonu sağlar.
Container Başlat
Suite başlangıcında production'a uygun database image çalıştırılır. Version açıkça pin edilebilir. Health check database'in gerçekten hazır olduğunu doğrular. Arbitrary sleep yerine readiness signal kullanılmalıdır. Startup cost suite seviyesinde paylaşılabilir.
Connection String Al
Container random port veya credential üretebilir. Test configuration runtime connection bilgisini container instance'tan alır. Hard-coded local port bağımlılığı ortadan kalkar. Parallel CI job'ları port collision yaşamaz. Secret test scope dışında kullanılmamalıdır.
Migration Çalıştır
Production migration pipeline'ında kullanılan aynı migration artefact'ı test database'e uygulanmalıdır. Böylece schema creation behavior ayrıca doğrulanır. Migration fail ederse test suite başlamamalıdır. Current schema version kontrol edilebilir. Schema drift erken yakalanır.
Test Data Hazırla
Her test minimum gerekli fixture'ı oluşturmalıdır. Büyük global seed debugging'i zorlaştırabilir. Factory veya builder valid default data üretir. Unique alanlar parallel testler için random veya deterministic namespace kullanabilir. Data ownership açık olmalıdır.
Testi Çalıştır
Repository, service veya HTTP endpoint gerçek database'e bağlanır. Test edilen davranış production code path üzerinden çalışır. Gereksiz external dependency'ler stub olabilir. Database interaction gerçek kalır. Transaction ve constraint behavior doğal biçimde ortaya çıkar.
Assert Et
Return value yanında database state gerekirse kontrol edilir. Hangi outcome'un business açısından önemli olduğu belirlenmelidir. Internal query count gibi implementation detaylarına gereksiz bağlanılmamalıdır. Constraint error bekleniyorsa doğru error mapping assert edilir. Failure anlaşılır mesaj vermelidir.
Container'ı Sil
Suite sonunda container kaldırılır. Test database kalıcı state bırakmaz. CI resource cleanup otomatik olur. Local debug için opsiyonel reuse ayrı ayar olabilir. Cleanup failure monitoring gerekebilir.
Integration Testlerde Test Data Yönetimi
Integration testlerin güvenilirliği büyük ölçüde test data yönetimine bağlıdır. Fixture, factory ve builder gibi pattern'ler minimum ve okunabilir setup oluşturur. Production dump kullanmak privacy, boyut ve determinism açısından ciddi problemler yaratabilir. Testler yalnızca ihtiyaç duydukları veriyi oluşturmalıdır. Deterministic test data hem parallel execution hem failure debugging için önemlidir.
Fixture
Fixture test öncesindeki bilinen başlangıç verisini temsil eder. Her test suite veya test için ayrı olabilir. Fixture çok büyürse hangi kaydın scenario için önemli olduğu anlaşılmaz. Minimum data tercih edilmelidir. Shared immutable lookup data bazı durumlarda ortak kullanılabilir.
Factory
Factory valid entity üretimini merkezi hale getirir. Test yalnızca önemli alanları override eder. Schema alanı eklendiğinde yüzlerce test setup'ını değiştirme ihtiyacı azalır. Factory business-invalid default üretmemelidir. Random değer kullanılıyorsa reproducibility korunmalıdır.
Builder
Builder okunabilir test data oluşturmak için fluent yapı sağlayabilir. Özellikle çok alanlı aggregate'larda faydalıdır. Testte kullanılan değerler business senaryosunu açıkça göstermelidir. Builder aşırı abstraction ile önemli setup'ı gizlememelidir. Valid default yaklaşımıyla iyi çalışır.
Seed Data
Seed data integration environment için temel lookup veya configuration sağlayabilir. Her test scenario'ya özel büyük seed kullanmak state coupling yaratabilir. Seed version migration ile uyumlu olmalıdır. Test gerekli business record'u kendisi oluşturmalıdır. Böylece execution order bağımsız kalır.
Minimal Test Data
Bir test yalnızca davranışı çalıştırmak için gerekli kayıtları hazırlamalıdır. Fazla data query sonucunu anlamayı zorlaştırır. Liste filtre testlerinde kontrol amaçlı birkaç ekstra kayıt anlamlı olabilir. Data miktarı scenario'yu açıklamalıdır. Minimal setup suite hızını da artırır.
Production Dump Kullanmanın Riskleri
Production dump kişisel veya hassas veri içerebilir. Aynı zamanda testleri büyük ve yavaş hale getirir. Data sürekli değiştiği için determinism kaybolur. Test gereksinimleri açık fixture yerine gizli production state'e bağlanır. Synthetic ve minimal test data çok daha güvenlidir.
Test Data Factory Pattern
Test Data Factory, testlerde tekrar eden entity setup kodunu azaltırken valid default nesne üretmeyi sağlar. Test yalnızca davranışı etkileyen alanları override eder. Bu yaklaşım testlerin okunabilirliğini yükseltir ve schema değişikliklerinden etkilenme alanını küçültür. Factory'nin business kuralını gizleyecek kadar akıllı olmaması önemlidir. Basit ve predictable factory uzun vadede en kullanışlı modeldir.
Valid Default Object
Factory varsayılan olarak geçerli entity üretmelidir. Test validation hatası istemiyorsa ekstra setup gerekmez. Default değerler mümkün olduğunca nötr seçilmelidir. Business açısından özel anlam taşıyan değerler test içinde açıkça override edilmelidir. Böylece scenario okunabilir kalır.
Teste Özel Override
Test yalnızca davranışı etkileyen field'ı değiştirir. Örneğin inactive user senaryosunda status override edilir. Geri kalan alanlar factory default'tan gelir. Setup satır sayısı azalır. Failure durumunda hangi input'un önemli olduğu kolay anlaşılır.
Duplicate Setup Kodunu Azaltmak
Entity constructor değiştiğinde yüzlerce testin setup'ını güncellemek gerekebilir. Factory değişikliği merkezi hale getirir. Ancak her setup satırını helper'a gizlemek doğru değildir. Yalnızca tekrar eden irrelevant data factory içinde bulunmalıdır. Business condition testte görünür kalmalıdır.
Testlerin Okunabilirliğini Artırmak
CreateValidOrder() gibi açık factory method test niyetini anlatır. Override edilen alan scenario'yu vurgular. Çok generic dictionary-based builder okunabilirliği düşürebilir. Type-safe helper tercih edilebilir. Test kodu production code kadar anlaşılır olmalıdır.
Her Integration Test Nasıl İzole Edilmeli?
Integration testlerin birbirinden bağımsız çalışması güvenilir suite için temel şarttır. Transaction rollback, database cleanup, schema-per-test veya ayrı container gibi farklı isolation teknikleri kullanılabilir. Hangi yöntemin uygun olduğu test edilen transaction davranışına ve runtime maliyetine bağlıdır. Test execution order sonucu değiştirmemelidir. Parallel run planlanıyorsa data collision riskleri de baştan çözülmelidir.
Transaction Rollback
Her test transaction içinde çalıştırılıp sonunda rollback edilebilir. Hızlı cleanup sağlar. Ancak application kendi transaction'ını yönetiyorsa test transaction gerçek behavior'ı değiştirebilir. Commit veya isolation davranışı test edilen senaryolarda uygun olmayabilir. Yöntemin sınırı bilinmelidir.
Database Cleanup
Test sonrası tablolar temizlenebilir veya truncate edilebilir. Foreign key order dikkate alınmalıdır. Cleanup logic test state'in bir sonraki teste taşınmasını engeller. Parallel testlerde shared database cleanup dikkat gerektirir. Suite seviyesinde reset tool kullanılabilir.
Schema-per-Test
Her test veya test worker ayrı schema kullanabilir. Parallel execution isolation sağlar. Migration veya schema creation maliyeti artabilir. Connection search path doğru yönetilmelidir. PostgreSQL projelerinde kullanışlı bir seçenek olabilir.
Database-per-Test
Her test ayrı database kullanırsa en güçlü logical isolation'lardan biri sağlanır. Startup ve migration maliyeti daha yüksek olabilir. Suite veya class seviyesinde database paylaşmak daha dengeli olabilir. Container engine hızlı database creation destekliyorsa uygulanabilir. CI parallelism ile birlikte planlanmalıdır.
Container-per-Test Suite
Bir test suite tek disposable container kullanabilir. Testler içeride cleanup veya schema isolation uygular. Startup maliyeti test başına container açmaktan daha düşüktür. Suite bittiğinde tüm state kaldırılır. Çoğu proje için iyi bir hız-isolation dengesi sağlar.
Testlerin Birbirine Bağımlı Olmaması
Test B'nin çalışması için Test A'nın önce data oluşturması gerekmemelidir. Test runner execution order değiştirebilir. Parallel mode bu dependency'yi hemen ortaya çıkarır. Her test kendi setup'ını üretmelidir. Bu prensip debugging ve retry davranışını da kolaylaştırır.
Parallel Integration Tests
Integration suite büyüdükçe paralel çalıştırma CI süresini önemli ölçüde azaltabilir. Ancak shared database ve sabit test data kullanımı collision yaratabilir. Unique data, ayrı schema veya ayrı container seçenekleri bu sorunu çözer. Parallelism yalnızca CPU sayısını artırmak değildir, dependency isolation da gerekir. Test timing ve resource capacity birlikte değerlendirilmelidir.
Shared Database Problemi
Birden fazla test aynı table ve aynı ID değerlerini kullanırsa birbirini etkileyebilir. Cleanup sırasında diğer testin data'sı silinebilir. Unique key collision flaky failure oluşturabilir. Shared database kullanılacaksa namespace stratejisi gerekir. Daha güçlü isolation için schema veya database ayrılabilir.
Data Collision
Sabit e-mail veya username birçok parallel testte duplicate constraint oluşturabilir. Test-specific unique suffix kullanılabilir. Deterministic worker ID iyi bir seçenektir. Tamamen random data debugging'i zorlaştırabileceği için seed saklanmalıdır. Factory parallel-safe değer üretmelidir.
Unique Test Data
Her test kendi business namespace'ini oluşturabilir. UUID veya test ID key'lere eklenebilir. Query yalnızca kendi data'sını kullanır. Cleanup targeted yapılabilir. Bu yöntem shared database parallelism için basit başlangıçtır.
Ayrı Schema
Worker başına schema data collision riskini önemli ölçüde azaltır. Migration schema içinde uygulanır. Parallel job sayısı arttığında schema creation maliyeti izlenmelidir. Connection configuration worker-specific olmalıdır. Suite sonunda schema'lar kaldırılır.
Ayrı Container
Her CI shard kendi database container'ına sahip olabilir. En güçlü environment isolation sağlar. Resource tüketimi artar. CI runner capacity buna göre planlanmalıdır. Testcontainers orchestration süreci basitleştirir.
CI Parallelism
Test suite birden fazla runner'a bölünebilir. Test timing verisine göre dengeli shard dağılımı yapılabilir. Her runner isolated dependency başlatabilir. Slowest shard toplam pipeline süresini belirler. Düzenli timing analizi dağılımı optimize eder.
Database Migration'ları Nasıl Test Ederiz?
Migration production database'i değiştirdiği için doğrudan test edilmesi gereken deployment artefact'ıdır. Empty database'den latest schema'ya çıkış kadar previous version'dan upgrade de test edilmelidir. Existing data bulunan migration senaryosu özellikle önemlidir. Destructive değişiklikler roll-forward veya rollback planıyla değerlendirilmelidir. Migration testi schema drift ve deployment sürprizlerini önemli ölçüde azaltır.
Empty Database → Latest Schema
Temiz database üzerinde tüm migration zinciri çalıştırılır. Yeni environment provisioning bu yol üzerinden ilerler. Migration sonrası expected tables ve constraints doğrulanabilir. Application smoke query çalıştırılır. Bu test sıfırdan kurulumun güvenilir olduğunu gösterir.
Previous Version → Latest Version
Production çoğunlukla boş database değildir. Önceki schema version hazırlanır ve target migration uygulanır. Application upgrade senaryosu gerçekçi biçimde test edilir. Version metadata kontrol edilir. Bu test deployment regression'larını yakalar.
Existing Data ile Migration
Migration mevcut veriyi dönüştürüyorsa representative fixture gerekir. Null veya edge data migration failure yaratabilir. Row count ve critical field sonuçları doğrulanmalıdır. Büyük data volume için performance testi ayrıca gerekebilir. Production dump yerine synthetic edge dataset tercih edilmelidir.
Constraint Validation
Yeni unique veya foreign key constraint existing data nedeniyle fail edebilir. Migration öncesi cleanup veya validation query test edilmelidir. Constraint gerçekten aktif mi integration test doğrular. Application error mapping yeni constraint ile uyumlu olmalıdır. Rollout planı lock süresini de dikkate almalıdır.
Destructive Migration
Column drop veya type change geri dönüşü zor olabilir. Expand-and-contract yaklaşımı riski azaltır. Test önce application'ın yeni schema ile çalıştığını göstermelidir. Backup veya roll-forward planı hazır olmalıdır. Destructive step mümkünse ayrı release'te uygulanmalıdır.
Rollback / Roll-Forward
Her migration kolayca rollback edilemeyebilir. Bu nedenle roll-forward fix çoğu production durumda daha güvenli olabilir. Test stratejisi kullanılan deployment modeline uygun olmalıdır. Backup restore süresi ayrıca bilinmelidir. Migration failure senaryosu runbook içinde tanımlanmalıdır.
API Integration Testleri
API integration test gerçek HTTP pipeline üzerinden request göndererek routing, middleware, validation ve response contract'ı birlikte doğrular. Controller method'unu doğrudan çağırmak bu katmanların tamamını kapsamaya yetmez. Authentication ve authorization behavior da gerçek pipeline içinde görülebilir. Backend uygulamalarda birim testi ve entegrasyon testi farkları özellikle controller testlerinde net biçimde ortaya çıkar. Business logic unit testte ayrıntılı korunurken endpoint test yalnızca transport ve integration davranışına odaklanabilir.
Gerçek HTTP Request Pipeline
Test client application'ın test host'una gerçek HTTP request gönderir. Middleware ve routing production'a yakın sırayla çalışır. Network socket kullanmak şart değildir. In-memory test server yeterli olabilir. Böylece framework integration gerçekten doğrulanır.
Routing
Route path ve HTTP method test edilmelidir. Yanlış route attribute unit controller testinde görünmeyebilir. Parameter binding ve route constraints gerçek request ile doğrulanır. Versioned API path'leri ayrıca test edilebilir. 404 ve 405 davranışı gerekirse kontrol edilir.
Middleware
Authentication, correlation ID veya exception middleware endpoint behavior'ını etkiler. Test pipeline bu component'leri gerçek sırayla çalıştırır. Bazı dış dependency'ler test configuration ile değiştirilebilir. Middleware order hataları bu seviyede yakalanabilir. Unit test bunu güvenilir biçimde göstermez.
Serialization
Request JSON gerçekten deserialize edilir ve response tekrar serialize edilir. Casing, enum ve custom converter hataları görünür hale gelir. Null behavior contract açısından test edilebilir. DTO object'i doğrudan controller'a vermek bu riski saklar. Integration test gerçek serializer configuration kullanmalıdır.
Validation
Invalid request body uygun status code ve error format üretmelidir. Framework validation annotation'ları gerçek pipeline'da doğrulanır. Domain validation ayrı unit testte ayrıntılı olabilir. Aynı kuralları iki seviyede bütün varyasyonlarla tekrar etmek gerekmez. API testi transport contract'a odaklanır.
Authentication
Anonymous request protected endpoint'e ulaşamamalıdır. Test authentication handler gerçek token validation veya kontrollü test auth provider kullanabilir. Kritik token parsing için ayrı integration test bulunabilir. Endpoint'in auth configuration'ı gerçek host üzerinde doğrulanır. Security testleri CI quality gate'e dahil edilmelidir.
Authorization
Authenticated olmak her resource'a erişim anlamına gelmez. Role ve ownership kuralları endpoint seviyesinde test edilmelidir. 401 ve 403 farkı doğru uygulanmalıdır. Resource başka kullanıcıya aitse erişim reddedilmelidir. Bu testler güvenlik sınırının gerçek application wiring'ini doğrular.
Response Contract
Status code, header ve JSON schema API contract'ın parçasıdır. Integration test response body içindeki önemli field'ları kontrol eder. Her alanı snapshot'a bağlamak gereksiz kırılma yaratabilir. Contract testing breaking değişiklikleri daha sistematik yakalayabilir. API documentation ile implementation uyumu ayrıca değerlendirilebilir.
Controller Unit Test mi API Integration Test mi?
Controller ince bir transport layer ise çok sayıda controller unit test çoğu zaman düşük değer üretir. Business logic service veya domain içinde unit test edilmelidir. Controller'ın route, binding, middleware ve HTTP response davranışı ise integration testte daha gerçekçi biçimde doğrulanır. Controller içinde business logic varsa önce bu logic'i daha uygun katmana taşımak düşünülebilir. Böylece test duplication azalır ve sorumluluklar netleşir.
Controller'da Business Logic Varsa
Controller pricing veya authorization kararları veriyorsa unit test ihtiyacı artabilir. Ancak bu durum design smell olabilir. Business logic domain veya application service'e taşındığında daha hızlı test edilir. Controller transport mapping'e odaklanır. API integration test yalnızca endpoint wiring'ini doğrular.
Framework Binding'i Test Etmek
Request body'nin DTO'ya doğru bind edilmesini controller method'u doğrudan çağırarak test edemezsiniz. Gerçek HTTP request gerekir. Validation ve converter behavior da aynı şekilde pipeline'a bağlıdır. Bu nedenle integration test daha anlamlıdır. Framework internals değil sizin configuration'ınız doğrulanır.
Route ve Middleware'i Test Etmek
Route attribute veya middleware order controller unit testte devreye girmez. Endpoint gerçek host üzerinden çağrılmalıdır. Authentication configuration da burada görünür olur. Bu test sayısı tüm business permutation'ları kapsamak zorunda değildir. Kritik route senaryoları yeterli confidence sağlayabilir.
HTTP Status ve Response Body
Controller'ın domain result'ı HTTP response'a doğru çevirmesi integration concern'dir. 201, 400 veya 404 gibi status behavior test edilebilir. Error contract serialize edilmiş haliyle doğrulanır. Domain error'ın kendisi unit testte ayrıca test edilmiş olabilir. Bu separation duplication'ı azaltır.
Test Duplication'dan Kaçınmak
Aynı business rule için 15 unit ve 15 API test yazmak gereksizdir. Unit test tüm edge case'leri kapsayabilir. API seviyesinde bir veya birkaç representative scenario transport wiring'i doğrular. E2E'de yalnızca kritik journey kullanılır. Her katmanın farklı sorusu olmalıdır.
Authentication ve Authorization Testleri
Authentication ve authorization backend güvenliğinin kritik test alanlarıdır. Anonymous ve authenticated request'ler yanında invalid veya expired token da test edilmelidir. Role-based ve resource ownership kuralları integration seviyesinde gerçek endpoint üzerinden doğrulanabilir. 401 ile 403 semantics'i doğru ayrılmalıdır. Production güvenlik bug'ları çoğu zaman business testlerden daha yüksek etkiye sahip olduğu için bu testler quality gate içinde öncelikli olmalıdır.
Anonymous Request
Protected endpoint'e token olmadan request gönderilir. Beklenen 401 veya uygun authentication response doğrulanır. Endpoint'in yanlışlıkla public hale gelmesi bu testle yakalanabilir. Response hassas data içermemelidir. Public endpoint'ler ayrı policy'ye sahip olmalıdır.
Authenticated Request
Geçerli kimlik ile allowed operation başarılı olmalıdır. Test identity fixture açık role ve user ID taşır. Authentication middleware gerçek veya kontrollü test implementation kullanabilir. Authorization success path ayrıca doğrulanır. Audit metadata gerekiyorsa test edilebilir.
Invalid Token
Geçersiz signature veya format ile request reddedilmelidir. Parser error application exception'a dönüşmemelidir. Response gereksiz security detayı vermemelidir. Token library'nin internals'ını test etmek gerekmez. Sizin authentication configuration'ınızın davranışı doğrulanır.
Expired Token
Expired token artık erişim sağlamamalıdır. Fixed clock veya test token helper kullanılabilir. Boundary expiration zamanı deterministic olmalıdır. Refresh flow varsa ayrı test edilir. Timezone bağımlılığı engellenmelidir.
Role-Based Access
Viewer ve admin gibi roller farklı permission'lara sahip olabilir. Aynı endpoint her role için expected sonucu üretmelidir. Role claim manipulation güvenilir authentication boundary'de engellenmelidir. Integration test gerçek authorization policy'yi çalıştırır. Business permission logic ayrıca unit test edilebilir.
Resource Ownership
Kullanıcı başka kullanıcıya ait resource ID ile request gönderdiğinde erişim reddedilmelidir. IDOR riskini azaltmak için bu test özellikle önemlidir. UUID kullanmak authorization'ın yerine geçmez. Repository query ownership scope taşımalıdır. Read, update ve delete operation'lar gerekirse ayrı test edilir.
401 vs 403
401 kimlik doğrulaması olmadığını veya geçersiz olduğunu ifade ederken 403 kimliği bilinen kullanıcının yetkisiz olduğunu gösterir. API contract bu ayrımı tutarlı uygulamalıdır. Client behavior bu status code'lara bağlı olabilir. Integration tests yanlış middleware order'ını yakalayabilir. Security documentation ile behavior uyumlu olmalıdır.
External API Integration Testleri
External API integration testlerinin amacı üçüncü taraf servisin tamamını test etmek değil, sizin client implementasyonunuzun contract'a uygun davranmasını doğrulamaktır. Gerçek API'ye her testte istek göndermek rate limit, maliyet ve availability sorunları yaratabilir. Sandbox bazı smoke senaryoları için faydalı olsa da deterministic test kaynağı olmayabilir. Local stub server request ve response contract'ını kontrollü biçimde çalıştırır. Failure senaryoları da aynı ortamda kolayca üretilebilir.
Gerçek Third-Party API'ye Test Göndermenin Riskleri
External servis geçici olarak down olabilir ve sizin kodunuz değişmediği halde test fail eder. Rate limit veya ücret oluşabilir. Test account state'i başka ekip tarafından değiştirilebilir. Network latency pipeline'ı yavaşlatır. Ana integration suite için controlled dependency daha güvenlidir.
Sandbox
Sandbox provider'ın gerçek API'ye yakın test ortamıdır. Contract ve authentication smoke testleri için faydalı olabilir. Ancak sandbox production ile tamamen aynı olmayabilir. Availability ve data state sizin kontrolünüzde değildir. Bu nedenle local tests yerine tamamlayıcı olarak kullanılmalıdır.
Local Stub Server
Local HTTP server expected endpoint ve response'ları kontrol eder. Application gerçek HTTP client ile bu server'a bağlanır. Serialization ve headers gerçekten çalışır. Timeout veya malformed response gibi senaryolar deterministically üretilebilir. Test suite external network'e ihtiyaç duymaz.
WireMock Benzeri Yaklaşımlar
HTTP stub server araçları request matching ve configurable response sağlar. Delay, status code ve body kolayca değiştirilebilir. Recorded production traffic yerine explicit contract tercih edilmelidir. Test expectation okunabilir configuration içinde tutulur. Language veya framework'e uygun alternatif araçlar kullanılabilir.
Request Contract
Method, path, query ve body doğru üretilmelidir. Authentication header veya idempotency key gerekiyorsa doğrulanır. Client model değişikliği external API contract'ını bozmamalıdır. Stub server gelen request'i assert edebilir. Bu test internal mock interaction'dan daha gerçekçi bir boundary doğrulamasıdır.
Response Parsing
Expected JSON doğru domain modeline çevrilmelidir. Optional veya unknown field behavior test edilebilir. Error response farklı schema kullanıyorsa ayrı case gerekir. Invalid JSON controlled failure üretmelidir. Parsing logic gerçek serializer üzerinden çalışmalıdır.
External API Failure Senaryolarını Test Etmek
Harici servis entegrasyonunda success path tek başına yeterli değildir. Timeout, connection error, rate limit ve server error production'da normal sayılabilecek olaylardır. Client'ın retry, fallback veya error mapping davranışı kontrollü biçimde test edilmelidir. Slow response ve partial body gibi durumlar da parsing veya timeout bug'larını ortaya çıkarabilir. Local stub veya network simulation bu senaryoları güvenli biçimde üretir.
Timeout
Stub response belirtilen timeout'tan daha geç döner. Client operation'ın beklenen sürede durduğu doğrulanır. Infinite wait oluşmamalıdır. Retry policy varsa toplam timeout ayrıca dikkate alınmalıdır. User-facing error uygun biçimde map edilmelidir.
Connection Error
Port kapalı veya connection reset durumu simüle edilebilir. Client bunu kontrollü exception'a çevirmelidir. Retry uygun dependency için devreye girebilir. Her connection error'ın tekrar denenmesi doğru olmayabilir. Failure classification açık olmalıdır.
429
Rate limit response retry-after header içerebilir. Client bu bilgiyi policy'ye göre değerlendirmelidir. Blind immediate retry servis yükünü artırabilir. Maximum retry sınırı test edilmelidir. Monitoring veya metric gerekiyorsa ayrıca doğrulanabilir.
500/503
Temporary server error retry adayı olabilir. Permanent behavior ve total attempt sayısı test edilmelidir. Circuit breaker threshold bu response'larla açılabilir. Fallback behavior varsa ayrıca doğrulanır. Test bekleme sürelerini gerçek saniyeler yerine fake clock ile hızlandırabilir.
Invalid JSON
HTTP 200 dönse bile body geçersiz olabilir. Client parsing error'ı doğru domain/infrastructure error'a çevirmelidir. Ham body sensitive ise loglanmamalıdır. Retry policy parsing error için farklı olabilir. Test malformed payload ile deterministically çalışır.
Partial Response
Required field eksik veya null olabilir. Parser ve validation behavior açık olmalıdır. Sessiz default value kullanmak riskli olabilir. Contract değişikliği bu testle yakalanabilir. Optional field ile required field ayrımı doğru modellenmelidir.
Slow Response
Response timeout altında fakat normalden yavaş olabilir. Performance budget açısından metric üretilebilir. Parallel request'lerde connection pool etkisi ayrı load test gerektirebilir. Integration test client timeout config'i doğrular. Slow dependency circuit breaker behavior'ını etkileyebilir.
Retry ve Circuit Breaker Testleri
Retry ve circuit breaker resilience mekanizmalarıdır fakat yanlış configuration duplicate side effect veya uzun latency yaratabilir. Testler ilk başarısız çağrıdan sonra success, maximum retry ve open circuit gibi davranışları açık biçimde doğrulamalıdır. Exponential backoff gerçek sleep ile test edilmemelidir. Clock veya scheduler abstraction test süresini kısaltır. Fallback yalnızca business açısından güvenli olduğunda kullanılmalıdır.
İlk Çağrı Başarısız Sonraki Başarılı
Stub ilk request'te 503, ikinci request'te success döndürür. Client'ın tam iki attempt yaptığı doğrulanabilir. Sonuç başarılı business response olmalıdır. Retry yalnızca idempotent operation için güvenli olmalıdır. Metric attempt count gösterebilir.
Maximum Retry
Dependency sürekli fail ettiğinde retry sınırsız devam etmemelidir. Maximum attempt sonrası final error dönmelidir. Toplam süre timeout budget içinde kalmalıdır. Unit seviyesinde scheduler fake kullanılabilir. Integration seviyesinde stub server attempt count doğrulayabilir.
Exponential Backoff
Backoff sequence clock abstraction ile test edilebilir. Gerçek saniyeler beklemek suite'i yavaşlatır. Jitter varsa deterministic random generator kullanılabilir. Policy expected delay pattern'i üretmelidir. Retry storm riskini azaltan davranış korunmalıdır.
Circuit Open
Failure threshold aşıldığında yeni request external servise gitmemelidir. Circuit hızlı fail etmelidir. Spy client call count bunu doğrulayabilir. Open duration controlled clock ile ilerletilebilir. Metric veya log behavior isteniyorsa test edilebilir.
Half-Open
Bekleme süresi sonunda sınırlı probe request gönderilir. Başarılı probe circuit'i kapatabilir. Başarısız probe tekrar open durumuna döndürür. Concurrent probe behavior implementation'a göre test edilebilir. Clock determinism önemlidir.
Fallback
Fallback kullanılıyorsa gerçek business anlamı olmalıdır. Cached data veya degraded response döndürülebilir. Sensitive operation'da fallback yanlış sonuç üretmemelidir. Test failure source ve fallback outcome'u birlikte doğrular. Observability sistemin degraded durumda olduğunu göstermelidir.
Contract Testing Nedir?
Contract testing consumer ile provider arasındaki API veya message sözleşmesinin uyumlu kaldığını doğrular. Tam end-to-end environment kurmadan breaking değişiklikleri daha erken yakalamaya yardımcı olur. Consumer hangi request ve response behavior'a ihtiyaç duyduğunu tanımlar, provider bu contract'ı kendi build pipeline'ında doğrular. Integration test tek servis içindeki gerçek boundary'ye odaklanırken contract test servisler arası beklentiyi korur. Mikroservislerde E2E test sayısını azaltmak için güçlü bir araç olabilir.
Consumer ve Provider
Consumer başka servisin API'sini kullanan taraftır. Provider bu API'yi sunar. Contract iki tarafın ortak beklentisini ifade eder. Consumer'ın hiç kullanmadığı tüm provider field'larını contract'a dahil etmek gerekmez. Böylece değişiklik özgürlüğü korunur.
API Contract
Request path, method, required field ve response shape contract'ın parçalarıdır. Status code davranışı da önemlidir. Contract implementation'a değil dış interface'e odaklanmalıdır. Version control altında tutulabilir. CI breaking değişikliği merge öncesinde yakalayabilir.
Consumer-Driven Contract
Consumer-driven modelde consumer gerçek ihtiyaç duyduğu interaction'ları tanımlar. Provider bu contract'ları kendi test pipeline'ında doğrular. Gereksiz geniş API şemasına bağlanma azalır. Birden fazla consumer farklı contract'lara sahip olabilir. Provider değişiklik yaparken etkilenen consumer'ları görebilir.
Integration Test'ten Farkı
Integration test genellikle sizin component'inizin dependency ile gerçekten çalışmasını kontrol eder. Contract test iki farklı deployment unit arasındaki sözleşmeyi doğrular. Gerçek network veya database gerekmeyebilir. Provider verification ayrı pipeline'da yapılabilir. İki test türü birbirini tamamlar.
End-to-End Test İhtiyacını Nasıl Azaltır?
Her servis kombinasyonunu tek staging environment'da E2E test etmek yavaş ve kırılgan olabilir. Contract test servis sınırını daha erken ve hızlı doğrular. Böylece E2E yalnızca kritik journey'lere ayrılabilir. Failure localization daha kolay olur. Microservice sayısı arttıkça bu avantaj daha belirgin hale gelir.
OpenAPI Contract Testing
OpenAPI contract testing implementation ile API specification arasında drift oluşmasını engellemeye yardımcı olur. Request schema, response schema ve status code'lar otomatik doğrulanabilir. Breaking API change CI sırasında tespit edilebilir. Specification yalnızca dokümantasyon dosyası değil executable contract haline gelir. API tasarımı ve standardizasyonu üzerine daha geniş bir backend bakışı için https://www.diyarbakiryazilim.com.tr/posts/graphql-performans-iyilestirmeleri-ve-n-1-problemi adresindeki teknik içerik de servis sınırlarında performans ve veri erişimi açısından tamamlayıcı bir perspektif sunabilir.
Request Schema
Required field, type ve format OpenAPI şemasına göre doğrulanır. Client geçersiz payload gönderdiğinde contract'a uygun error beklenir. Implementation yeni field eklediğinde backward compatibility değerlendirilir. Schema validation generated tests ile desteklenebilir. Domain validation yine ayrı logic olarak ele alınır.
Response Schema
Endpoint response specification'daki field ve type'larla uyumlu olmalıdır. Yanlış casing veya missing required field contract testte görünür. Optional field değişikliği breaking olmayabilir. Consumer compatibility açık biçimde değerlendirilmelidir. Snapshot test yerine schema validation daha esnek olabilir.
Status Codes
Specification endpoint'in hangi status code'ları üretebileceğini tanımlayabilir. Implementation beklenmeyen 500 veya yanlış 200 döndürürse test fail eder. Error contract da schema ile birlikte doğrulanabilir. Authentication response'ları ayrıca kapsanabilir. Bu yaklaşım client beklentisini daha güvenilir tutar.
Breaking API Change
Required field eklemek veya response field kaldırmak consumer'ı bozabilir. Contract verification değişikliği release öncesinde görünür hale getirir. Versioning veya deprecation kararı verilebilir. Her değişiklik otomatik olarak yasaklanmamalıdır. Ama etkisi bilinmeden production'a çıkmamalıdır.
API Implementation ile Spec Drift
Dokümantasyon güncel değilse consumer yanlış contract'a göre geliştirme yapar. Automated comparison drift riskini azaltır. Code-first veya spec-first yaklaşımın ikisi de kullanılabilir. CI tek doğru contract kaynağını doğrulamalıdır. Documentation ve test aynı kaynağı kullanırsa bakım kolaylaşır.
Message Queue Integration Tests
Message queue integration testleri publish, consume, serialization ve acknowledgement davranışını gerçek broker üzerinden doğrular. RabbitMQ veya Kafka gibi sistemlerin semantics'i basit in-memory fake ile tam temsil edilemez. Event routing ve dead letter configuration gerçek integration concern'dir. Testcontainers disposable broker kurulumunu kolaylaştırabilir. Eventual consistency nedeniyle sleep yerine explicit condition waiting tercih edilmelidir.
RabbitMQ
Exchange, queue ve binding configuration test environment'da gerçekten oluşturulabilir. Message publish edilir ve consumer sonucu beklenir. Acknowledgement failure veya dead letter flow test edilebilir. Routing key doğru configuration ile doğrulanır. Container cleanup suite sonunda yapılır.
Kafka
Topic ve partition behavior integration testin parçası olabilir. Producer ve consumer gerçek serialization kullanır. Consumer group offset behavior belirli senaryolarda test edilebilir. Ordering yalnızca partition sınırında garanti edildiği için test buna uygun tasarlanmalıdır. Full cluster performance ayrı load test konusudur.
Publish
Application event'i doğru topic veya queue'ya göndermelidir. Header ve payload schema doğrulanabilir. Transactional outbox kullanılıyorsa database-event consistency ayrıca test edilir. Publish failure error handling'i çalıştırabilir. Internal producer call count yerine broker'da gerçek message bulunması daha yüksek güven sağlar.
Consume
Consumer gerçek broker'dan message alır ve business side effect üretir. Duplicate message davranışı ayrıca test edilmelidir. Consumer error sonrası acknowledgement policy doğru olmalıdır. Test result database veya emitted event üzerinden doğrulanabilir. Timeout kontrollü tutulmalıdır.
Serialization
Event object gerçek serializer ile bytes veya JSON'a çevrilmelidir. Consumer aynı schema'yı okuyabilmelidir. Enum veya date format değişikliği breaking olabilir. Contract tests message schema için ek güven sağlar. Integration test gerçek serializer configuration'ını doğrular.
Acknowledgement
Başarılı işlemden sonra mesaj ack edilmelidir. Failure durumunda requeue veya dead letter policy net olmalıdır. Ack erken verilirse işlem tamamlanmadan message kaybolabilir. Test failure injection ile bu sınırı doğrulayabilir. Broker semantics gerçek dependency gerektirir.
Dead Letter Queue
Sürekli başarısız message belirli policy sonunda DLQ'ya gitmelidir. Original metadata korunmalıdır. Support veya replay process bunu kullanabilir. DLQ routing configuration integration testle doğrulanır. Infinite retry önlenmelidir.
Event-Driven Sistemlerde Test Stratejisi
Event-driven backend testlerinde yalnızca “message yayınlandı mı?” sorusu yeterli değildir. Event schema, consumer contract, duplicate event ve ordering gibi konular production güvenilirliğini doğrudan etkiler. Eventual consistency testlerinde sabit sleep kullanmak flaky sonuçlar üretir. Idempotent consumer aynı event iki kez geldiğinde side effect'i tekrar üretmemelidir. Contract ve broker integration testlerini birlikte kullanmak güçlü bir test portföyü oluşturur.
Event Schema
Event envelope version, type ve gerekli field'ları tanımlar. Breaking field değişiklikleri consumer'ları etkileyebilir. Schema validation veya contract test kullanılabilir. Unknown field backward compatibility açısından kabul edilebilir olabilir. Event versioning stratejisi açık olmalıdır.
Consumer Contract
Consumer hangi event field'larına ihtiyaç duyduğunu belirtir. Producer değişikliği bu contract'ı bozmamalıdır. Contract provider verification ile korunabilir. Consumer'ın kullanmadığı tüm producer field'ları testte zorunlu hale getirilmemelidir. Böylece bağımsız deployment esnekliği korunur.
Eventual Consistency
Consumer işlemi asynchronous olduğu için sonuç hemen oluşmayabilir. Test explicit condition belirli timeout içinde sağlanana kadar beklemelidir. Sabit iki saniye sleep hem yavaş hem flaky olabilir. Poll interval küçük tutulabilir. Timeout failure diagnostic bilgi vermelidir.
Duplicate Event
Aynı event broker tarafından veya retry nedeniyle tekrar gelebilir. Consumer duplicate operation üretmemelidir. Event ID veya business key idempotency için kullanılabilir. Test aynı message'i iki kez publish eder. Database side effect sayısı bir olmalıdır.
Out-of-Order Event
Distributed sistemlerde event order her zaman garanti edilmeyebilir. Consumer eski version event'i yeni event'ten sonra alabilir. State transition buna dayanıklı tasarlanmalıdır. Test order'ı bilinçli ters çevirerek behavior'ı doğrular. Domain requirement order guarantee gerektiriyorsa broker configuration ayrıca test edilir.
Idempotent Consumer
Idempotent consumer aynı input tekrarlandığında yeni side effect üretmez. Processed event table veya unique constraint kullanılabilir. Concurrency altında iki aynı event aynı anda gelirse race condition oluşabilir. Gerçek database integration test bu riski doğrular. Unit test yalnızca high-level logic'i kapsayabilir.
Redis ve Cache Integration Tests
Cache testleri hit, miss, TTL ve invalidation davranışlarını gerçek Redis instance üzerinde doğrulayabilir. In-memory dictionary expiration ve serialization semantics'i tam temsil etmez. Cache bug'ları stale data veya yanlış user data gösterme gibi ciddi sonuçlara yol açabilir. Test key namespace ve invalidation flow'u açık biçimde çalıştırmalıdır. Containerized Redis hızlı ve disposable test dependency sağlar.
Cache Hit
Cache içinde veri bulunduğunda backend database'e gereksiz tekrar gitmemelidir. Behavior gerektiğinde spy repository ile doğrulanabilir. Return edilen data doğru deserialize edilmelidir. TTL henüz dolmamış olmalıdır. Cache key doğru scope'u kullanmalıdır.
Cache Miss
Key bulunmadığında source dependency çağrılır ve sonuç cache'e yazılabilir. İlk ve ikinci request davranışı birlikte test edilebilir. Missing value caching policy ayrıca değerlendirilebilir. Failure durumunda yanlış null cache oluşmamalıdır. Test gerçek Redis key state'ini kontrol edebilir.
TTL
TTL expiration production semantics'e bağlıdır. Gerçek Redis integration test kullanmak değerlidir. Test sürelerini çok uzun tutmamak gerekir. Gerekirse kısa test TTL seçilir. Expired key sonrasında source data yeniden yüklenmelidir.
Eviction
Manual eviction business update sonrasında gerekli olabilir. Resource değiştiğinde ilgili key silinmelidir. Birden fazla related cache varsa hepsi doğru scope'ta temizlenmelidir. Global flush test isolation dışında kullanılmamalıdır. Event-driven invalidation ayrıca broker testleriyle birleşebilir.
Cache Invalidation
Cache invalidation update sonrası stale data riskini yönetir. Integration test önce cache'i doldurur, data'yı değiştirir ve yeniden request yapar. Yeni değer dönmelidir. Wrong key veya missing invalidation kolayca yakalanır. Bu test production bug'larında yüksek değer sağlar.
Serialization
Complex object Redis'e JSON veya binary formatta yazılabilir. Version değişikliğinde eski cache entry deserialize edilemeyebilir. Integration test real serializer kullanmalıdır. Backward compatibility gerekiyorsa representative old payload kullanılabilir. Sensitive field cache'e gereksiz yazılmamalıdır.
Background Job ve Worker Testleri
Background job testlerinde scheduling, execution, retry ve idempotency birlikte düşünülmelidir. Worker HTTP request dışında çalıştığı için dependency configuration hataları kolayca gözden kaçabilir. Unit test job logic'i hızlı doğrular, integration test gerçek database ve queue boundary'sini çalıştırır. Failure durumunda partial side effect oluşmaması önemlidir. Scheduled job'larda clock abstraction deterministic test sağlar.
Job Scheduling
Job doğru zaman veya event sonrasında queue'ya eklenmelidir. Clock fixed olabilir. Duplicate schedule idempotency gerektirebilir. Cron expression configuration test edilebilir. Scheduler library'nin internals'ını tekrar test etmek gerekmez.
Job Execution
Worker message'i alıp business operation'ı çalıştırır. Integration test gerçek queue ve database kullanabilir. Output side effect doğrulanır. Job context gerekli correlation metadata'yı taşımalıdır. Failure logları teşhis için anlamlı olmalıdır.
Retry
Temporary failure sonrasında job tekrar çalışabilir. Maximum retry ve backoff policy test edilmelidir. Aynı side effect iki kez oluşmamalıdır. Retry state queue metadata'sında korunabilir. Permanent error DLQ'ya yönlendirilebilir.
Failure
Dependency failure partial database state bırakmamalıdır. Transaction boundary gerektiğinde test edilmelidir. Error monitoring veya metric üretimi kontrol edilebilir. Job failed state'e geçebilir. Kullanıcıya yanlış success sinyali gönderilmemelidir.
Idempotency
Aynı job message iki kez çalıştırıldığında duplicate side effect oluşmamalıdır. Unique business key veya processed message ID kullanılabilir. Test concurrent duplicate execution da çalıştırabilir. Real database constraint önemli güvence sağlar. Idempotency yalnızca unit mock ile doğrulanmamalıdır.
Database Side Effects
Worker database'e kayıt yazıyorsa gerçek repository integration test faydalıdır. Job success sonrasında expected rows doğrulanır. Failure rollback behavior test edilir. Event ve database consistency gerekiyorsa outbox yaklaşımı değerlendirilebilir. Side effect count açık biçimde assert edilmelidir.
Idempotency Nasıl Test Edilir?
Idempotency aynı işlem birden fazla kez tetiklendiğinde beklenmeyen duplicate side effect oluşmamasıdır. Payment, webhook ve message consumer sistemlerinde kritik öneme sahiptir. Test aynı request veya event'i iki kez çalıştırmalı ve sonuçta tek business operation oluştuğunu doğrulamalıdır. Concurrency altında iki aynı request aynı anda geldiğinde de aynı garanti korunmalıdır. Database unique constraint çoğu zaman application check'ten daha güçlü koruma sağlar.
Aynı Request'i İki Kez Göndermek
Aynı idempotency key ile endpoint iki kez çağrılır. İkinci request yeni işlem üretmemelidir. Response ilk işlem sonucu ile uyumlu olabilir. Database'de tek resource bulunmalıdır. External payment gateway call sayısı da bir olmalıdır.
Aynı Event'i İki Kez Consume Etmek
Aynı event ID broker'a iki kez gönderilir. Consumer yalnızca bir side effect üretmelidir. Processed event kaydı unique constraint ile korunabilir. Concurrent consume scenario ayrıca test edilebilir. Ack behavior doğru kalmalıdır.
Duplicate Payment
Payment gibi irreversible operation'larda duplicate request büyük risk taşır. Idempotency key external provider'a da iletilebilir. Test iki request sonrası tek charge oluştuğunu doğrular. Timeout sonrası client retry senaryosu özellikle önemlidir. Payment record state machine ayrıca unit test edilebilir.
Idempotency Key
Key scope ve expiration açık olmalıdır. Aynı key farklı payload ile kullanılırsa policy belirlenmelidir. Database unique index atomicity sağlar. Cache tek başına güçlü source of truth olmayabilir. Integration test gerçek constraint behavior'ını doğrulamalıdır.
Side Effect Sayısını Doğrulamak
Idempotency sonucu yalnızca response ile ölçülmemelidir. Database insert, e-mail veya external payment call count kontrol edilebilir. Interaction burada business sonucunun önemli parçasıdır. Spy veya stub server kullanılabilir. Concurrent testte toplam side effect yine bir olmalıdır.
Concurrency ve Race Condition Testleri
Concurrency bug'ları sequential unit testlerde çoğu zaman görünmez. Aynı resource'a paralel write, lost update ve unique constraint race gerçek database integration testi gerektirebilir. Testleri deterministic yapmak için barrier veya synchronization primitive kullanılmalıdır. Rastgele thread timing'e güvenmek flaky test üretir. Kritik para ve stok işlemleri concurrency açısından ayrıca test edilmelidir.
Aynı Kaynağa Paralel Yazma
İki request aynı row'u aynı anda güncelleyebilir. Test iki transaction'ı kontrollü noktada paralel başlatır. Beklenen conflict veya serialized outcome doğrulanır. Final state business invariant'ı korumalıdır. Database isolation seviyesi açık olmalıdır.
Lost Update
İki client eski değeri okuyup farklı update yazarsa ilk değişiklik kaybolabilir. Optimistic version column bunu engelleyebilir. Test aynı version ile iki update gönderir. Biri success, diğeri conflict olmalıdır. Gerçek database transaction behavior gereklidir.
Optimistic Lock
Version veya timestamp field update sırasında kontrol edilir. İlk writer version'ı artırır. İkinci writer eski version ile fail eder. API uygun 409 veya domain error döndürebilir. Integration test ORM concurrency mapping'ini doğrular.
Unique Constraint Race
İki request aynı unique value için önce “var mı?” query'si yapabilir. İkisi de boş görüp insert'e geçebilir. Database unique constraint yalnızca birinin başarılı olmasını sağlar. Test paralel insert ile gerçek behavior'ı doğrular. Application constraint error'ını güvenli biçimde map etmelidir.
Distributed Lock
Distributed lock kullanılıyorsa aynı resource key için yalnızca bir worker critical section'a girmelidir. Lock expiry ve crash behavior ayrıca test edilmelidir. Redis gibi gerçek dependency gerekebilir. Lock correctness'i yalnızca mock ile kanıtlamak zordur. Mümkün olduğunda database atomic primitive'leri daha sade olabilir.
Testleri Deterministic Hale Getirmek
Race testinde yalnızca “100 kez çalıştır belki fail eder” yaklaşımı zayıftır. Barrier ile iki execution belirli noktada bekletilebilir. Sonra aynı anda devam ettirilir. Böylece race window kontrollü oluşur. Failure tekrar üretilebilir hale gelir.
Test Pyramid Nedir?
Test Pyramid alt katmanda çok sayıda hızlı unit test, orta katmanda daha az integration test ve üstte az sayıda E2E test öneren düşünsel modeldir. Amaç test sayısını matematiksel yüzdeyle belirlemek değil hızlı feedback ile production confidence arasında sağlıklı denge kurmaktır. Alt testler ucuz olduğu için business edge case'leri burada ayrıntılı kapsanır. Üst katmanlar daha pahalı olduğu için kritik entegrasyon ve journey'lere ayrılır. Backend test stratejisinde test pyramid nasıl uygulanır sorusunun cevabı sistem mimarisine ve riskine göre değişir.
Unit Tests
Unit testler pyramid'in geniş tabanını oluşturabilir. Çok hızlı oldukları için birçok business scenario'yu kapsarlar. Failure localization kolaydır. External dependency kullanmazlar. Ancak boundary integration hatalarını tek başına yakalayamazlar.
Integration Tests
Integration testler gerçek dependency sınırlarını doğrular. Unit testlere göre daha yavaş ve setup açısından pahalıdır. Database ve API gibi kritik boundary'lerde kullanılır. Her business permutation'ı tekrar etmemelidir. Production confidence açısından büyük değer sağlar.
End-to-End Tests
E2E tüm sistem veya büyük bölümünü kullanıcı journey üzerinden çalıştırır. En yüksek environment ve bakım maliyetine sahip test türlerindendir. Kritik happy path için değerlidir. Çok fazla kullanılırsa feedback yavaşlar. Failure nedenini bulmak zorlaşabilir.
Test Sayısı ve Maliyet İlişkisi
Test seviyesi yükseldikçe çoğunlukla runtime ve maintenance cost artar. Unit test milisaniyelerde, broad E2E ise dakikalar içinde çalışabilir. Bu nedenle detaylı permutation alt seviyelerde tutulur. Üst seviyede representative confidence senaryoları seçilir. Amaç en ucuz testle yeterli güveni elde etmektir.
Neden Alt Katmanda Daha Fazla Test Bulunur?
Çünkü unit test hızlı, deterministic ve failure diagnosis açısından ucuzdur. Business logic varyasyonlarını burada çalıştırmak verimlidir. Aynı 30 scenario'yu E2E'de test etmek pipeline'ı gereksiz uzatır. Ancak alt katmanın geniş olması integration katmanının ihmal edilmesi anlamına gelmez. Her layer farklı risk sorusunu yanıtlar.
70/20/10 Test Dağılımı Bir Kural mı?
70/20/10 gibi test oranları başlangıç düşüncesi verebilir fakat evrensel mühendislik kuralı değildir. Database-heavy backend ile saf calculation library aynı test dağılımına ihtiyaç duymaz. Microservice architecture contract testing'e daha fazla yatırım yapabilir. Legacy monolith broad integration testlerden daha fazla fayda görebilir. Sabit oran yerine risk, runtime ve production fidelity üzerinden karar vermek daha doğru sonuç verir.
Sabit Oranların Sınırlamaları
Yüzde hedefi testlerin kalitesini veya risk coverage'ını göstermez. Yüzlerce düşük değerli unit test kritik database hatasını engellemeyebilir. Test dağılımı sistem boundary'lerine göre şekillenmelidir. Oran yalnızca conversation starter olarak kullanılabilir. Quality gate'e dönüşmesi gereksiz davranışlara yol açabilir.
Sistemin Mimarisine Göre Dağılım
Domain-heavy application daha fazla unit test kullanabilir. Data-heavy service gerçek database integration testlerine daha fazla ihtiyaç duyabilir. Event-driven sistem broker ve contract testlerine yatırım yapar. Her architecture farklı failure mode taşır. Test portföyü bu failure mode'lara göre kurulmalıdır.
Microservice ve Monolith Farkı
Microservice sistemde servisler arası contract önemli risk alanıdır. Contract tests E2E ihtiyacını azaltabilir. Monolith içinde in-process integration daha ucuz olabilir. Database boundary yine gerçek dependency gerektirir. Mimari değiştikçe test dağılımı da değişmelidir.
Risk Bazlı Test Portföyü
Test bütçesi high-impact behavior'a yönlendirilmelidir. Payment idempotency için unit, integration ve belki E2E birlikte mantıklı olabilir. Trivial admin filter için tek integration test yeterli olabilir. Risk analizi test seviyesini belirler. Bu yaklaşım sabit oranlardan daha gerçekçidir.
Test Pyramid'in Alternatifleri
Test Pyramid tek görsel model değildir. Testing Trophy, Honeycomb ve Diamond gibi alternatifler integration testlerin önemini farklı biçimde vurgular. Bu modeller arasında “hangisi mutlak doğru?” tartışmasından çok ekip için hangi trade-off'un önemli olduğuna bakmak gerekir. Modern container ve test server araçları integration test maliyetini geçmişe göre düşürmüştür. Bu nedenle test portföyü kullanılan teknoloji ve architecture'a göre yeniden dengelenebilir.
Testing Trophy
Testing Trophy integration testlere daha güçlü ağırlık veren bir yaklaşımdır. Özellikle web application davranışında component'lerin birlikte çalışmasının önemli olduğunu vurgular. Unit test yine business logic için değerlidir. E2E daha sınırlı tutulur. Backend projelerinde database ve HTTP integration için benzer düşünce kullanılabilir.
Testing Honeycomb
Honeycomb modeli distributed sistemlerde integration ve observability bakışını öne çıkarabilir. Servis sınırlarının gerçek davranışı önemlidir. Unit testler local logic'i korur. Contract ve integration testler servis ilişkilerini doğrular. Büyük E2E environment'a bağımlılık azaltılır.
Test Diamond
Diamond modeli integration testlerin test portföyünde geniş yer alabileceğini savunan bir başka görsel yaklaşımdır. Modern backendlerde gerçek dependency container kullanmak bu fikri daha uygulanabilir hale getirir. Her repository method'u unit mock yerine gerçek database ile test edilebilir. Yine de business permutation unit seviyesinde daha ucuzdur. Denge sistem riskine göre kurulmalıdır.
Hangi Şeklin “Doğru” Olduğundan Çok Trade-Off'lara Odaklanmak
Bir görsel modele bağlı kalmak test stratejisini gereksiz katı hale getirebilir. Hız, maintenance, reliability ve fidelity gerçek karar kriterleridir. Test size yerine sağladığı güven değerlendirilmelidir. Aynı proje içinde farklı modüller farklı test profiline sahip olabilir. Test architecture yaşayan bir mühendislik kararıdır.
SMURF ile Test Portföyü Nasıl Değerlendirilir?
SMURF yaklaşımı test portföyünü yalnızca test seviyesine göre değil speed, maintainability, utilization, reliability ve fidelity açısından değerlendirmeye yardım eder. “Kaç test?” sorusundan daha anlamlı olan “hangi test ne kadar güvenilir ve ne kadar hızlı feedback veriyor?” sorusudur. Bir broad integration test yavaş olabilir fakat çok yüksek production fidelity sağlayabilir. Bir unit test hızlıdır fakat gerçek database riskini kapsamaz. Test kararını bu trade-off'larla vermek daha olgun bir strateji oluşturur.
Speed
Testin sonuç üretme süresi geliştirici feedback kalitesini etkiler. Her test suite için runtime budget belirlenebilir. Fast tests merge request içinde çalışır. Slow suite daha seyrek stage'e taşınabilir. Speed uğruna kritik fidelity kaybedilmemelidir.
Maintainability
Test kodu production code kadar bakım gerektirir. Aşırı mock veya büyük snapshot'lar maintenance cost'u artırabilir. Behavior-focused testler refactor'a daha dayanıklıdır. Fixture ve helper bilinçli kullanılmalıdır. Kırılgan testler uzun vadede güven kaybettirir.
Utilization
Test gerçekten ne sıklıkta ve hangi karar için kullanılıyor sorusu önemlidir. Geliştiricilerin local çalıştırmadığı çok ağır suite geç feedback verir. Merge quality gate'te kullanılan test daha yüksek utilization'a sahiptir. Nightly testler belirli riskleri kapsayabilir. Kullanılmayan test raporları kalite üretmez.
Reliability
Aynı kodda testin her seferinde aynı sonucu vermesi gerekir. Flaky test güvenilir değildir. Developer zamanla failure'ı görmezden gelmeye başlar. External shared environment reliability'yi düşürebilir. Disposable dependency ve deterministic input bu metriği iyileştirir.
Fidelity
Fidelity test environment'ın production behavior'ını ne kadar temsil ettiğini gösterir. Real PostgreSQL container yüksek database fidelity sağlar. Mock repository daha düşük fidelity ile daha yüksek speed sunar. Her test için maksimum fidelity gerekli değildir. Kritik boundary'lerde yüksek fidelity daha değerlidir.
End-to-End Testler Nerede Kullanılmalı?
E2E testler bütün sistemi veya büyük bir kullanıcı journey'sini birlikte doğrulamak için kullanılmalıdır. Authentication ve payment gibi kritik happy path'ler uygun adaylardır. Her edge case'i E2E seviyesinde çalıştırmak suite'i yavaş ve kırılgan hale getirir. Business permutation unit ve integration seviyelerinde daha ucuz biçimde korunabilir. E2E'nin temel amacı sistem parçalarının en kritik akışlarda birlikte çalıştığını göstermektir.
Kritik Happy Path
Kullanıcının ana değer akışı E2E test için iyi adaydır. Örneğin kayıt, sipariş ve confirmation birlikte çalıştırılabilir. Test yalnızca en önemli journey'yi kapsar. Minor validation case'ler alt seviyede test edilir. Failure business kritik flow'un gerçekten bozulduğunu gösterir.
Authentication Flow
Login, token ve protected page/API zinciri E2E olarak doğrulanabilir. Identity provider external ise test environment configuration gerekir. Her invalid password varyasyonu E2E'de bulunmak zorunda değildir. Kritik sign-in ve sign-out akışları yeterli olabilir. Authorization edge case'leri API integration seviyesinde daha hızlı test edilir.
Payment Flow
Payment kullanıcı açısından kritik journey'dir. Test provider sandbox veya controlled emulator kullanabilir. Tam production charge yapılmamalıdır. Success ve bir kritik failure scenario E2E olabilir. Idempotency ve retry ayrıntıları alt integration seviyesinde daha kapsamlı test edilir.
Sistemin Tamamının Birlikte Çalışması
E2E deployment, routing ve service wiring'in ayakta olduğunu gösterir. Birden fazla service, database ve frontend birlikte çalışabilir. Failure localization zor olduğu için sayısı sınırlı tutulmalıdır. Observability test debugging'i kolaylaştırır. Smoke suite deployment sonrasında da kullanılabilir.
Neden Her Senaryo E2E Olmamalı?
E2E startup ve environment maliyeti yüksektir. Network ve async dependency nedeniyle flaky olma ihtimali daha fazladır. Failure hangi katmandan kaynaklanıyor anlamak daha zordur. Test feedback süresi uzar. Edge case'leri alt katmanlarda doğrulamak çok daha verimlidir.
Ice Cream Cone Anti-Pattern
Ice Cream Cone anti-pattern az unit test ve çok sayıda manual veya E2E test bulunan test dağılımını ifade eder. Böyle ekiplerde feedback geç gelir ve test bakım maliyeti hızla büyür. Basit business hatası bile uzun environment setup sonrasında fark edilir. Flaky E2E testler zamanla pipeline güvenini düşürür. Çözüm business logic'i hızlı test katmanına taşımak ve E2E'yi kritik journey'lerle sınırlamaktır.
Çok Fazla Manuel/E2E Test
Aynı validation senaryosunu UI üzerinden onlarca kez test etmek pahalıdır. Browser veya distributed environment her run'da ek failure noktaları oluşturur. Regression feedback'i yavaşlar. Birçok scenario unit veya API integration seviyesine taşınabilir. Manual testing exploratory ihtiyaçlara ayrılabilir.
Az Unit Test
Business rules hızlı test edilmediğinde küçük değişikliklerin sonucu geç görülür. Developer refactor konusunda daha az güven hisseder. Edge case coverage düşer. Unit test tabanı kritik domain logic'i ayrıntılı korumalıdır. Ancak yalnız unit test de yeterli değildir.
Yavaş Feedback
Developer her değişiklikte 30 dakika pipeline bekliyorsa iteration hızı düşer. Failure geç görüldüğü için context switching artar. Stage'ler hıza göre ayrılmalıdır. Fast tests önce çalışmalıdır. Test runtime sürekli izlenmelidir.
Yüksek Bakım Maliyeti
E2E testler environment ve UI/API contract değişikliklerinden daha fazla etkilenebilir. Fixture setup uzun olabilir. Test data conflict flaky behavior yaratabilir. Aynı risk daha düşük test seviyesinde korunabiliyorsa E2E kaldırılabilir. Test portföyü düzenli sadeleştirilmelidir.
Test Hourglass Anti-Pattern
Test Hourglass çok sayıda unit ve E2E test bulunurken integration katmanının zayıf kaldığı test dağılımıdır. Unit testler mocklarla yeşil kalabilir, E2E ise boundary hatasını ancak çok geç ve geniş scope'ta yakalar. Database ve API integration eksikliği failure localization'ı zorlaştırır. Modern backend projelerinde bu boşluk gerçek dependency integration testleriyle doldurulmalıdır. Böylece component boundary sorunları daha erken ve daha anlaşılır biçimde bulunur.
Çok Unit Test
Unit test sayısının yüksek olması tek başına sorun değildir. Problem tüm infrastructure boundary'nin mocklanmasıdır. Repository ve API client gerçek davranışı hiç test edilmeyebilir. Yeşil unit suite false confidence oluşturur. Critical integration noktaları ayrıca doğrulanmalıdır.
Çok E2E Test
Boundary güveni yalnız E2E üzerinden sağlanmaya çalışılırsa failure geç bulunur. Test environment karmaşık hale gelir. Her küçük database mapping bug'ı full journey'i bozabilir. Narrow integration test aynı problemi daha hızlı teşhis eder. E2E kritik akışlarla sınırlanmalıdır.
Eksik Integration Katmanı
Database, queue veya serializer real behavior'ı test edilmez. Mock contract ve production contract arasında fark oluşabilir. Integration suite bu boşluğu kapatır. Testcontainers setup maliyetini azaltabilir. Feedback ve fidelity dengesi iyileşir.
Component Boundary Hatalarının Geç Bulunması
ORM mapping veya serialization bug'ı unit testte görünmeyebilir. E2E ise ancak sistem ayağa kalktıktan sonra fail eder. Narrow integration test bu hatayı component seviyesinde yakalar. Debugging süresi azalır. CI stage daha hızlı feedback verir.
Flaky Test Nedir?
Flaky test aynı code version üzerinde bazen geçip bazen başarısız olan testtir. Shared state, sistem zamanı, network ve async race en sık nedenler arasındadır. Flaky testler pipeline güvenini ciddi biçimde düşürür çünkü ekip bir süre sonra gerçek failure'ları da görmezden gelmeye başlayabilir. Testi otomatik retry etmek temel problemi çözmez. Flakiness production bug kadar ciddi teknik borç olarak ele alınmalıdır.
Aynı Kodda Bazen Geçip Bazen Kalma
Test sonucu kod değişmeden farklıysa determinism problemi vardır. Önce failure rate ölçülmelidir. Test tekrar çalıştırma root cause belirlemek için yardımcı olabilir. Ancak success retry sonucu merge'e izin vermek riski gizler. Testin nedeni bulunup düzeltilmelidir.
Shared State
İki test aynı database record veya global singleton state'i değiştirebilir. Execution order sonucu etkiler. Parallel run problemi daha sık görünür hale getirir. Test kendi data ve cleanup lifecycle'ına sahip olmalıdır. Disposable infrastructure shared state riskini azaltır.
Time
Current time boundary test sırasında milisaniye içinde değişebilir. Expiration testi zaman zaman farklı branch'e girebilir. Fixed clock kullanmak gerekir. Timezone CI ortamında farklı olabilir. Test tüm zamanı explicit yönetmelidir.
Network
External API veya shared staging geçici olarak yavaşlayabilir. Test timeout nedeniyle rastgele fail eder. Local stub veya container bağımlılığı daha güvenilirdir. Gerçek external smoke test ayrı pipeline'a alınabilir. Core merge testleri dış availability'ye bağlanmamalıdır.
Randomness
Random test data collision veya farklı path üretebilir. Seed saklanmıyorsa failure yeniden üretilemez. Controlled random generator tercih edilmelidir. Property-based framework failure seed'i raporlar. Pure random behavior test suite'i belirsiz hale getirir.
Async Race
Asynchronous işlemi sabit sleep ile beklemek flaky olabilir. Hızlı makinede yeterli, yavaş CI runner'da yetersiz kalabilir. Explicit condition polling kullanılmalıdır. Timeout makul üst sınır sağlar. Eventual consistency testleri deterministic wait helper kullanmalıdır.
External Dependency
Shared Redis, database veya sandbox başka testlerden etkilenebilir. State kontrolünüz dışında değişir. Containerized ephemeral dependency isolation sağlar. External provider testleri ayrı smoke stage'e taşınabilir. Core suite kendi dependency lifecycle'ını yönetmelidir.
Flaky Testler Nasıl Önlenir?
Flaky testleri önlemek için isolation ve determinism test tasarımının temel ilkeleri olmalıdır. Sistem saati, randomness ve async waiting kontrollü hale getirilmelidir. Shared environment yerine disposable infrastructure kullanmak state kaynaklı failure'ları azaltır. Network dependency mümkün olduğunca local ve controlled olmalıdır. Flaky test tespit edildiğinde owner atanmalı ve sorun kalıcı biçimde çözülmelidir.
Isolation
Her test kendi data'sını oluşturmalı ve başka test state'ine bağlı olmamalıdır. Database cleanup veya schema isolation kullanılabilir. Global mutable singleton'lardan kaçınılmalıdır. Parallel run isolation eksiklerini görünür hale getirir. Test order değiştirilse bile sonuç aynı kalmalıdır.
Deterministic Clock
Fixed clock time-sensitive behavior'ı kontrol eder. Test runtime saatine güvenmez. Expiration ve schedule boundary açık biçimde ilerletilebilir. CI timezone farkı etkisiz hale gelir. Production implementation gerçek clock kullanır.
Controlled Randomness
Random generator test tarafından enjekte edilebilir. Property-based tests seed kaydetmelidir. Unique data için worker/test ID kullanılabilir. Tamamen rastgele e-mail üretip failure'ı tekrar üretememek kötü debugging deneyimi yaratır. Randomness kontrol altında tutulmalıdır.
Explicit Waiting
Async sonuç için sabit sleep yerine beklenen condition kontrol edilmelidir. Polling belirli interval ve timeout kullanır. Sonuç erken oluşursa test hemen devam eder. Yavaş CI runner'da gereksiz failure azalır. Timeout mesajı beklenen condition'ı göstermelidir.
Shared Environment'tan Kaçınmak
Staging database'i integration test için paylaşmak state conflict yaratır. Başka developer veya pipeline data'yı değiştirebilir. Test environment reproducible değildir. Local container dependency daha güçlü isolation sağlar. Shared staging yalnız daha geniş smoke test için kullanılabilir.
Disposable Infrastructure
Her suite fresh database veya broker başlatabilir. Dependency version controlled olur. Test sonrası state kaldırılır. Local ve CI aynı setup'ı kullanır. Flakiness ve onboarding problemleri azalır.
Başarısız Testi Otomatik Retry Etmek Doğru mu?
Automatic retry bazı geçici infrastructure sorunlarında teşhis amacıyla kullanılabilir, ancak flaky test sorununu gizlemek için varsayılan çözüm olmamalıdır. İlk run fail edip ikinci run geçiyorsa pipeline yeşil görünse bile güven problemi devam eder. Flaky testler ayrı metric ile izlenmelidir. Gerekirse geçici quarantine uygulanabilir. Her quarantine test için owner ve düzeltme süresi belirlenmelidir.
Retry'nin Flakiness'i Gizlemesi
Test her beş çalıştırmada bir fail ediyorsa retry çoğu run'ı yeşile çevirebilir. Ekip problemi fark etmeyebilir. Gerçek production regression da flaky zannedilebilir. İlk-attempt failure ayrıca raporlanmalıdır. Retry quality gate'i tamamen bypass etmemelidir.
Retry Sadece Teşhis İçin Kullanılmalı mı?
Debugging sırasında testi tekrar çalıştırmak failure pattern'ini anlamaya yardım eder. CI sistemi flaky adaylarını birkaç kez çalıştırıp istatistik üretebilir. Fakat merge decision açık policy'ye dayanmalıdır. Critical test first-run failure veriyorsa dikkat gerektirir. Retry root cause çözümünün yerine geçmez.
Flaky Test Quarantine
Quarantine geçici olarak testin main quality gate'i bloklamamasını sağlayabilir. Test suite'ten tamamen silinmemelidir. Ayrı pipeline'da çalışmaya devam eder. Owner ve issue link'i bulunmalıdır. Süresiz quarantine teknik borcu kalıcı hale getirir.
Owner Atamak
Flaky test modül veya ekip sorumlusuna atanmalıdır. Failure rate ve son görülme tarihi izlenebilir. Sahipsiz test kolayca unutulur. Dashboard test health'i görünür hale getirebilir. Ownership kalite sorumluluğunu netleştirir.
Fix SLA
Kritik suite'teki flaky test için kabul edilebilir düzeltme süresi belirlenebilir. Security veya payment testleri daha kısa SLA alabilir. Flakiness seviyesine göre öncelik verilir. Quarantine süresi sınırlanır. Test reliability engineering metriği olarak izlenir.
Test Coverage Nedir?
Test coverage test execution sırasında production kodunun hangi bölümünün çalıştığını ölçen bir sinyaldir. Line, branch, function ve statement coverage farklı perspektif sağlar. Changed-code coverage yeni değişen kodun test kapsamını özellikle ölçmek için yararlıdır. Ancak coverage yüksekliği assertion kalitesini veya business risk coverage'ını garanti etmez. Backend test stratejisinde coverage bir hedef değil yön gösteren bir ölçüm olarak kullanılmalıdır.
Line Coverage
Hangi source line'ların test sırasında çalıştığını gösterir. Kolay anlaşılır bir metriktir. Ancak satır çalışmış olsa bile doğru sonuç assert edilmemiş olabilir. Branch behavior hakkında sınırlı bilgi verir. Tek başına kalite göstergesi değildir.
Branch Coverage
If veya switch gibi branch'lerin farklı yönlerinin çalıştırılıp çalıştırılmadığını ölçer. Conditional logic için line coverage'dan daha anlamlı olabilir. Her branch'e girilmesi doğru assertion olduğu anlamına gelmez. Business risk ile birlikte değerlendirilmelidir. Critical rule'larda useful signal sağlar.
Function Coverage
Hangi function veya method'ların en az bir kez çağrıldığını gösterir. Trivial method sayısı metriği yapay biçimde etkileyebilir. Behavior coverage'ın yerini tutmaz. Legacy code'da untested module bulmak için kullanılabilir. Quality gate olarak tek başına yeterli değildir.
Statement Coverage
Çalıştırılan statement oranını gösterir. Dil ve coverage tool'a göre line coverage ile benzer görünebilir. Complex expression tek statement içinde birden fazla branch taşıyabilir. Bu nedenle branch coverage tamamlayıcıdır. Yine de boş bölgeleri keşfetmek için faydalıdır.
Changed-Code Coverage
Changed-code coverage yalnızca yeni veya değiştirilen kod satırlarına odaklanır. Büyük legacy codebase'de global coverage hedefinden daha uygulanabilir olabilir. Yeni feature'ın testsiz merge edilmesini azaltır. Ancak riskli eski kod tamamen gözden kaçmamalıdır. Regression history test önceliğini etkileyebilir.
%100 Code Coverage İyi Test Anlamına Gelir mi?
Hayır, yüzde yüz coverage otomatik olarak güçlü test suite anlamına gelmez. Test hiçbir meaningful assertion yapmadan bütün satırları çalıştırabilir. Yalnız happy path kapsanabilir ve edge case'ler gözden kaçabilir. Business riski coverage metriğinden bağımsız değerlendirilmelidir. Coverage eksik alanı gösteren bir sinyal olarak değerlidir, ancak kaliteyi tek başına ölçemez.
Assertion Olmadan Coverage
Bir method çağrılır ve sonuç kontrol edilmezse coverage artabilir. Test behavior'ı korumaz. Production code yanlış sonuç üretse bile test geçer. Mutation testing bu zayıflığı ortaya çıkarabilir. Coverage ile assertion quality birlikte düşünülmelidir.
Happy Path Ağırlığı
Her function sadece başarılı input ile çalıştırılırsa line coverage yüksek olabilir. Failure branch ve boundary durumları test edilmemiş kalır. Branch coverage biraz daha iyi sinyal verir. Yine de business edge case analizi gereklidir. Risk bazlı test tasarımı vazgeçilmezdir.
Edge Case Eksikliği
Coverage hangi input kombinasyonlarının test edildiğini göstermez. Aynı line onlarca farklı edge condition taşıyabilir. Boundary ve property-based testing bu açığı tamamlar. Production bug history yeni edge case'ler öğretir. Coverage düşük riskli bir rehberdir.
Business Risk Coverage
Ödeme ve authorization kodu yüzde 80 coverage ile güçlü test edilebilirken trivial mapper yüzde 100 olabilir. Hangi sayı daha değerli sorusu business impact'e bağlıdır. Risk matrix coverage dashboard'u tamamlamalıdır. Critical scenarios açık test listesiyle izlenebilir. Sayısal metrik bağlam olmadan yanıltıcıdır.
Coverage'ı Goal Değil Signal Olarak Kullanmak
Coverage düşüşü yeni kodun testsiz kalabileceğini gösterebilir. Ancak geliştiricileri yalnız yüzdeyi yükseltmeye zorlamak düşük değerli test üretir. Changed-code coverage daha anlamlı gate olabilir. Critical modules için farklı threshold kullanılabilir. Test review behavior kalitesine odaklanmalıdır.
Mutation Testing Nedir?
Mutation testing production kodunda kontrollü küçük değişiklikler yaparak testlerin bu hataları yakalayıp yakalamadığını ölçer. Örneğin > operatörü >= olarak değiştirilebilir. Test fail ederse mutant öldürülmüş kabul edilir. Test geçmeye devam ederse assertion veya test scenario eksik olabilir. Mutation score coverage'ın ölçemediği test etkisini anlamaya yardımcı olur.
Production Kodunu Kontrollü Değiştirmek
Mutation tool source code üzerinde geçici değişiklik oluşturur. Gerçek repository kalıcı olarak değiştirilmez. Her mutation için ilgili testler çalıştırılır. Bu işlem normal testten daha yavaştır. Critical module veya nightly pipeline için uygun olabilir.
Mutant
Mutant original code'un küçük değiştirilmiş versiyonudur. Conditional veya return value farklılaştırılabilir. Amaç gerçekçi bug benzeri değişiklik üretmektir. Equivalent mutant sonucu gerçekte değiştirmeyebilir. Tool raporu manuel yorum gerektirebilir.
Killed Mutant
Değişiklik en az bir testi fail ettiriyorsa mutant killed sayılır. Bu testin ilgili davranışı gerçekten koruduğunu gösterir. Hangi testin mutantı öldürdüğü analiz edilebilir. Yüksek kill rate güçlü assertion sinyali sağlar. Tek başına yine kalite garantisi değildir.
Survived Mutant
Production behavior değiştirilmesine rağmen tüm testler geçerse mutant survive eder. Eksik assertion veya scenario olabilir. Bazen mutation business sonucunu etkilemeyen implementation detail'dir. Critical survived mutant'lar yeni test fırsatı gösterir. Review risk bazlı yapılmalıdır.
Test Assertion Kalitesini Ölçmek
Coverage yalnız kodun çalıştığını gösterirken mutation testing davranış değişikliği yakalanıyor mu sorusuna yaklaşır. Bu nedenle test quality için daha güçlü sinyal olabilir. Runtime maliyeti yüksektir. Her commit yerine changed modules üzerinde kullanılabilir. Critical domain logic için özellikle faydalıdır.
Unit Test ile Integration Test Arasında Test Duplication Nasıl Önlenir?
Test duplication aynı business permutation'ın unit, integration ve E2E katmanlarında tekrar tekrar yazılmasıdır. Bu yaklaşım test sayısını artırır fakat maintenance cost'u da büyütür. Business rule ayrıntısı unit testte korunmalıdır. Integration test boundary davranışına ve E2E kritik journey'ye odaklanmalıdır. Her test seviyesi farklı bir soruyu cevapladığında duplication doğal biçimde azalır.
Aynı Business Permutation'ı Her Katmanda Tekrarlamamak
İndirim kuralının 20 farklı input kombinasyonu unit testte çalıştırılabilir. API integration seviyesinde bir successful ve bir invalid örnek yeterli olabilir. E2E'de yalnız ana satış journey'si kullanılır. Böylece her katmanın amacı farklı kalır. Değişiklikte aynı expectation onlarca yerde güncellenmez.
Business Logic'i Unit Testlerde Ayrıntılı Test Etmek
Unit test hızlı olduğu için edge case sayısı burada yüksek olabilir. Domain behavior bağımsız doğrulanır. Database veya HTTP setup gerektirmez. Failure doğrudan business rule'a işaret eder. Üst test katmanları yalnız wiring ve boundary risklerine odaklanır.
Integration Layer'da Boundary Davranışına Odaklanmak
Repository testi SQL ve constraint'i doğrular. API testi routing ve serialization'ı doğrular. Aynı calculation edge case'ini burada tekrar etmek gerekmez. Representative data yeterli olabilir. Boundary test değişiklikleri daha anlamlı hale gelir.
E2E'de Yalnızca Kritik Journey'leri Test Etmek
E2E suite kullanıcıya en değerli flow'ları kapsamalıdır. Her validation message veya database constraint E2E seviyesinde test edilmemelidir. Böylece suite küçük ve güvenilir kalır. Pipeline runtime düşer. Failure gerçekten kritik sistem entegrasyonunu gösterir.
Testlerin Okunabilirliği ve Bakımı
Test kodu geçici script değildir ve production code kadar bakım gerektirir. Aşırı abstraction testin senaryosunu gizleyebilir, aşırı copy-paste ise değişiklik maliyetini büyütebilir. Fixture, builder ve helper doğru seviyede kullanılmalıdır. Test başarısız olduğunda setup ve expectation hızlı anlaşılmalıdır. Okunabilir test suite yeni geliştiricilerin sistemi öğrenmesini de kolaylaştırır.
Test de Production Code'dur
Test kodu version control ve code review sürecinin parçasıdır. Naming, duplication ve design kararları önemlidir. Kırık helper onlarca testi etkileyebilir. Test utilities ayrı maintenance gerektirir. Kalitesiz test code zamanla production geliştirme hızını düşürür.
Gereksiz Abstraction'dan Kaçınmak
Üç satırlık setup'ı beş helper arkasına gizlemek testi okumayı zorlaştırabilir. Abstraction yalnız tekrar eden irrelevant detayları saklamalıdır. Business condition test body'de görünmelidir. Generic test framework oluşturma isteği kontrol edilmelidir. Sadeliğin çoğu zaman daha yüksek değeri vardır.
Test Helper
Authentication token veya HTTP request setup gibi tekrar eden teknik detay helper'a alınabilir. Helper default davranışı predictable olmalıdır. Test-specific override desteklenebilir. Helper failure message'i gizlememelidir. Çok geniş “doEverything” helper'lardan kaçınılmalıdır.
Fixture
Fixture test context'i açık biçimde hazırlar. Shared fixture expensive dependency setup için faydalı olabilir. Mutable business state testler arasında paylaşılmamalıdır. Fixture scope bilinçli seçilmelidir. Cleanup lifecycle test framework'e uygun tasarlanmalıdır.
Builder
Builder complex domain object setup'ını okunabilir hale getirir. Valid default ve scenario override yapısı kullanışlıdır. Business-relevant field'lar testte açık kalmalıdır. Builder hidden magic üretmemelidir. Schema değişikliklerini merkezi yönetir.
Copy-Paste ve DRY Dengesi
Production code'daki kadar agresif DRY testlerde her zaman iyi değildir. Birkaç tekrar test senaryosunu daha açık hale getirebilir. Çok büyük duplicated setup ise helper gerektirebilir. Test readability en önemli kriterdir. Refactor maliyeti gerçek tekrar görüldüğünde değerlendirilmelidir.
Test Klasör Yapısı Nasıl Olmalı?
Test klasör yapısı unit, integration, contract ve E2E suite'lerin ayrı çalıştırılabilmesini kolaylaştırmalıdır. Framework veya language convention'ı başlangıç noktası olarak kullanılabilir. Test türünün runtime ve dependency ihtiyacı directory veya project seviyesinde ayrılabilir. CI bu separation sayesinde farklı stage'lerde farklı suite çalıştırabilir. İsimlendirme developer'ın ilgili testleri kolayca bulmasını sağlamalıdır.
Unit Tests
Unit testler source module'lara yakın veya ayrı unit project içinde tutulabilir. External infrastructure dependency'si olmamalıdır. Çok hızlı çalışmalıdır. Naming domain behavior'ını göstermelidir. CI'nın ilk test stage'inde çalıştırılabilir.
Integration Tests
Integration suite database veya broker setup içerir. Ayrı project/module dependency yönetimini kolaylaştırabilir. Testcontainers configuration burada bulunabilir. Runtime unit suite'ten daha uzundur. CI ayrı stage üzerinden çalıştırır.
Contract Tests
Consumer ve provider contract artefact'ları açık directory içinde tutulabilir. Provider verification ayrı komutla çalışabilmelidir. Version ve publication configuration görünür olmalıdır. Contract tests integration suite'ten bağımsız deploy coordination sağlayabilir. CI stage ayrımı faydalıdır.
E2E Tests
E2E testler full environment veya deployed service dependency'si taşıyabilir. Sayıları sınırlı tutulmalıdır. Test data provisioning ayrı helper içerebilir. Smoke subset deployment sonrası çalıştırılabilir. Full suite main veya nightly pipeline'a yerleştirilebilir.
Framework/Language Bazlı Ayrım
JUnit, pytest veya xUnit gibi framework convention'ları runner discovery davranışını etkiler. Proje yapısı standard tool'larla uyumlu olmalıdır. Custom script sayısı azaltılmalıdır. Test category/tag kullanılabilir. Basit komutlarla ilgili suite seçilebilmelidir.
CI'da Ayrı Çalıştırılabilir Hale Getirmek
Unit ve integration testlerin tek komutta zorunlu birlikte çalışması feedback yönetimini zorlaştırır. Suite category veya project separation kullanılabilir. Merge request önce hızlı testleri çalıştırır. Heavy suite parallel stage'e alınabilir. Failure hangi test seviyesinde problem olduğunu doğrudan gösterir.
CI/CD Pipeline'da Testler Nasıl Çalıştırılmalı?
CI/CD pipeline testleri feedback süresine ve risk seviyesine göre stage'lere ayırmalıdır. Lint ve static checks en hızlı kontroller olduğu için önce çalışabilir. Unit testlerden sonra fast integration suite, ardından daha geniş integration ve contract testleri gelebilir. E2E veya smoke test en pahalı katman olarak sona yakın çalıştırılır. Bu sıralama basit hataları pahalı environment kurulmadan önce yakalar.
Stage 1 — Lint ve Static Checks
Format, type check ve static analysis çok hızlı feedback sağlar. Compile error veya obvious issue burada yakalanır. Container başlatmaya gerek yoktur. Security linter veya dependency check de eklenebilir. Başarısızsa pipeline erken durabilir.
Stage 2 — Unit Tests
Unit testler hızlı olduğu için ikinci stage'de çalıştırılabilir. Domain regression'larının büyük bölümü burada görülür. Coverage raporu üretilebilir. Parallel test runner kullanılabilir. Failure developer'a birkaç dakika içinde ulaşmalıdır.
Stage 3 — Fast Integration Tests
Narrow database veya API tests bu stage'de bulunabilir. Container startup optimize edilmiştir. Suite merge request feedback budget içinde kalmalıdır. Critical security integration tests burada çalışabilir. Failure daha ağır stage'leri gereksiz çalıştırmadan pipeline'ı durdurabilir.
Stage 4 — Full Integration Tests
Daha geniş database, queue ve external stub senaryoları burada yer alabilir. Parallel sharding runtime'ı azaltır. Dependency isolation önemlidir. Main branch veya merge request policy'ye göre çalıştırılabilir. High-risk backend projelerinde merge gate olabilir.
Stage 5 — Contract Tests
Consumer contract ve provider verification çalıştırılır. Breaking service change merge öncesinde görülür. Contract broker veya artefact registry kullanılabilir. Failure hangi consumer'ın etkilendiğini göstermelidir. Microservice deployment güveni artar.
Stage 6 — E2E / Smoke Tests
Deployment environment üzerinde kritik journey'ler çalıştırılabilir. Suite küçük tutulur. Success sistemin temel parçalarının birlikte çalıştığını gösterir. Full E2E daha uzun ise nightly pipeline'a ayrılabilir. Smoke failure production rollout'u durdurabilir.
Testleri Hızına Göre Pipeline'a Yerleştirmek
Test türü kadar testin ne kadar sürede feedback verdiği de pipeline tasarımında önemlidir. Sub-second testler developer iteration sırasında sürekli çalışabilir. Saniyeler süren integration testler merge request içinde rahatlıkla yer alabilir. Dakikalar süren broad suite'ler paralel veya daha geç stage'e taşınabilir. Test runtime budget düzenli ölçülürse suite büyüdükçe pipeline kontrol altında tutulabilir.
Sub-Second Tests
Saf function ve domain unit testleri bu gruptadır. Local development sırasında sürekli çalıştırılabilir. Çok sayıda case ucuzdur. Failure localization hızlıdır. Bu testlerin network veya disk I/O kullanmaması beklenir.
Seconds
Narrow integration veya application test server suite birkaç saniye sürebilir. Merge request için hala hızlı feedback sayılır. Container reuse startup maliyetini azaltır. Test sayısı arttıkça parallel execution değerlendirilebilir. Critical boundary tests burada tutulabilir.
Minutes
Broad integration ve E2E suite dakikalar sürebilir. Her commit'te developer local olarak çalıştırmak istemeyebilir. CI parallelism önem kazanır. Main branch veya pre-deploy gate olarak kullanılabilir. Runtime trend izlenmelidir.
Merge Request Pipeline
Developer merge kararı için gerekli kritik testler burada olmalıdır. Unit, changed-code coverage ve fast integration tests genellikle uygundur. Contract compatibility de burada çalışabilir. Pipeline çok uzun olursa developer feedback süresi düşer. Riskli modül için selective heavy tests tetiklenebilir.
Main Branch Pipeline
Merge sonrası daha geniş suite çalıştırılabilir. Full integration ve E2E testler eklenebilir. Build artefact yalnız bu suite geçtikten sonra release adayı olabilir. Parallel execution süreci hızlandırır. Failure hızlı bildirilmelidir.
Nightly Suite
Mutation testing, uzun property-based runs veya geniş compatibility matrix nightly çalışabilir. Bu testler merge feedback'ini yavaşlatmadan ek güven sağlar. Failure sabah net owner'a atanmalıdır. Nightly suite sahipsiz rapor üretmemelidir. Trend metrikleri teknik borcu gösterir.
CI'da Integration Test Environment
CI integration environment reproducible ve isolated olmalıdır. Docker veya Testcontainers gerçek dependency'leri job ömrü boyunca çalıştırabilir. Shared staging environment state ve availability problemi nedeniyle ana integration suite için iyi seçenek değildir. Ephemeral environment her pipeline'ın aynı başlangıç koşuluna sahip olmasını sağlar. Local developer setup ile CI arasında mümkün olduğunca aynı configuration kullanılmalıdır.
Docker
Database ve broker standard image üzerinden çalıştırılabilir. Version pin etmek reproducibility sağlar. CI service container özelliği kullanılabilir. Health check readiness'i doğrular. Test code connection bilgilerini environment üzerinden alır.
Docker Compose
Birden fazla dependency birlikte ayağa kaldırılabilir. Local development için anlaşılır configuration sunar. Parallel CI job'larda network ve project name isolation gerekir. Cleanup garanti edilmelidir. Büyük test suite için Testcontainers daha dinamik kontrol sunabilir.
Testcontainers
Test code dependency lifecycle'ını doğrudan yönetir. Random port ve cleanup otomatik hale gelir. Per-suite veya per-class container kullanılabilir. Local ve CI aynı test library üzerinden çalışır. Programmatic configuration integration setup'ı testle birlikte versionlar.
Ephemeral Environment
Pipeline için oluşturulan environment test tamamlanınca silinir. Shared state problemi azalır. Full service deployment gerektiğinde namespace veya temporary stack kullanılabilir. Cost lifecycle ile kontrol altında tutulur. Security secret'ları kısa ömürlü olmalıdır.
Shared Staging Ortamından Kaçınmak
Birden fazla pipeline aynı staging database'i kullanırsa test state karışır. Bir deployment diğer pipeline testini bozabilir. External manual tester data'yı değiştirebilir. Failure reproducible olmaz. Staging daha çok exploratory ve release smoke test için kullanılmalıdır.
Test Parallelization ve Sharding
Test suite büyüdüğünde tek runner üzerinde serial çalıştırmak pipeline süresini gereksiz uzatabilir. Suite test file veya historical timing'e göre shard'lara bölünebilir. Her shard isolated database veya dependency kullanmalıdır. Slowest shard toplam zamanı belirlediği için eşit test sayısı yerine runtime dengesi önemlidir. Parallelism infrastructure kapasitesiyle uyumlu planlanmalıdır.
Test Suite'i Parçalara Bölmek
Unit, integration veya module bazında shard yapılabilir. Her parça bağımsız çalışmalıdır. Shared global state olmamalıdır. Test runner category desteği kullanılabilir. Shard sayısı suite büyüklüğüne göre ayarlanır.
CI Runner Parallelism
Birden fazla runner aynı anda farklı test grubunu çalıştırır. Total wall-clock time düşer. Runner sayısı arttıkça database container resource tüketimi de büyür. Cost ve speed birlikte ölçülmelidir. Bottleneck yalnız CPU olmayabilir.
Test Timing Bazlı Sharding
Historical test duration kullanılarak shard'lar dengelenebilir. Bir shard 20 dakika diğerleri 3 dakika sürmemelidir. CI tool veya custom script timing data kullanabilir. Test runtime değiştikçe dağılım güncellenir. Pipeline efficiency artar.
Integration Dependency Isolation
Her shard aynı database'i paylaşmamalıdır. Ayrı container veya schema kullanılabilir. Port ve data collision engellenmelidir. External stub server shard-specific olabilir. Isolation olmadan parallelism flaky testleri artırır.
Pipeline Süresini Azaltmak
Parallelism tek çözüm değildir. Duplicate E2E testleri kaldırmak ve container startup reuse etmek de önemlidir. Slow test raporu regular review edilmelidir. Test caching bazı deterministic suite'lerde kullanılabilir. Feedback hedefi engineering metric olarak izlenmelidir.
Merge Request Quality Gates
Merge request quality gate yeni kodun minimum güvenlik ve kalite standartlarını karşılamasını sağlar. Unit ve integration testlerin geçmesi temel koşuldur. Changed-code coverage yeni davranışın testsiz eklenmesini azaltabilir. Security ve contract compatibility testleri critical backend projelerinde ayrıca gate olabilir. Flaky testleri retry ile görmezden gelmek quality gate'in değerini düşürür.
Unit Tests Pass
Domain regression varsa merge engellenmelidir. Unit suite hızlı olduğu için gate maliyeti düşüktür. Test failure açık owner tarafından çözülmelidir. Skipped critical test izlenmelidir. Suite sürekli güvenilir tutulmalıdır.
Integration Tests Pass
Database ve API boundary değişiklikleri gerçek dependency üzerinde doğrulanmalıdır. Migration error veya auth misconfiguration merge öncesinde yakalanır. Flaky integration test gate güvenini bozabilir. Isolation ve deterministic setup önemlidir. High-risk projectlerde full suite zorunlu olabilir.
Changed-Code Coverage
Yeni değişen code için minimum coverage signal kullanılabilir. Global legacy percentage developer'ı gereksiz düşük değerli test yazmaya itmemelidir. Critical branch için manual review gerekir. Coverage düşüşü açıklama isteyebilir. Sayı test kalitesinin yerine geçmemelidir.
Critical Security Tests
Authentication, authorization ve IDOR tests her merge'de çalışabilir. Security failure yüksek impact taşıdığı için gate olmalıdır. New endpoint protected mı kontrol edilir. Dependency security scan ayrı stage olabilir. Security test ownership açık tutulmalıdır.
Contract Compatibility
Provider breaking change consumer'ı bozuyorsa merge engellenebilir. Contract verification hızlı feedback sağlar. Versioned intentional break için controlled override policy bulunabilir. Deprecation plan açık olmalıdır. Contract artefact current consumer expectations'ı temsil etmelidir.
Flaky Test Olmaması
Gate testleri güvenilir olmalıdır. First-run failure rate düzenli izlenebilir. Flaky test quarantine geçici çözüm olabilir. Owner ve fix deadline bulunmalıdır. Sürekli retry edilen test quality gate olmaktan çıkar.
Production Bug Sonrası Test Stratejisi
Production bug yalnızca kodu düzeltme işi değildir, test portföyünün hangi riski kaçırdığını gösteren önemli bir geri bildirimdir. Önce bug'ı tekrar üreten test yazılmalıdır. Testin hangi seviyede bulunması gerektiği root cause'a göre seçilir. Daha sonra fix uygulanır ve regression test kalıcı suite'e eklenir. Her incident test strategy review için veri üretmelidir.
Önce Bug'ı Reproduce Eden Test
Issue local veya test environment'da yeniden üretilmelidir. En düşük uygun test seviyesi seçilir. Database bug'ıysa gerçek database integration test gerekebilir. Business calculation ise unit test yeterli olabilir. Reproduction olmadan fix'in gerçekten doğru problem için çalıştığını kanıtlamak zorlaşır.
Testin Başarısız Olduğunu Görmek
Yeni test mevcut bug'lı kod üzerinde fail etmelidir. Baştan geçen test problemi gerçekten reproduce etmiyor olabilir. Failure expected reason'a sahip olmalıdır. Bu adım testin değerini doğrular. Sonra bug fix uygulanır.
Bug Fix
Fix mümkün olan en dar davranış değişikliğini yapmalıdır. Regression test success vermelidir. Mevcut suite'de beklenmeyen failure'lar değerlendirilir. Root cause yanlış abstraction ise refactor gerekebilir. Fix sonrasında code review test ile birlikte yapılmalıdır.
Regression Test
Reproduction test suite'te kalıcı olmalıdır. Test adı incident'i değil business davranışını açıklamalıdır. Aynı bug tekrar döndüğünde CI yakalar. Test data minimal hale getirilebilir. Incident ID yorum olarak gerekiyorsa eklenebilir.
Hangi Test Katmanında Eksik Olduğunu Analiz Etmek
Bug unit logic mi, database boundary mi, contract mı yoksa deployment wiring mi sorusu sorulmalıdır. Yanlış test seviyesine onlarca yeni test eklemek yerine gerçek boşluk kapatılmalıdır. Bir production SQL bug'ını repository mock testleriyle çözmeye çalışmak yeterli olmaz. Incident trend aynı test gap'ini tekrar gösteriyorsa strateji değişmelidir. Test maturity bu öğrenme döngüsüyle artar.
Backend Test Stratejisinde Sık Yapılan Hatalar
Backend test stratejisinde sık görülen hatalar test sayısından çok yanlış test sınırı seçimiyle ilgilidir. Her class'ı unit test etmek, her dependency'yi mocklamak veya production PostgreSQL kullanırken testte SQLite tercih etmek önemli riskler oluşturabilir. Shared integration database ve flaky retry alışkanlığı da zamanla suite güvenini azaltır. Coverage'ı tek kalite metriği yapmak ekipleri anlamsız testler yazmaya yöneltebilir. Sağlıklı strateji behavior, gerçek boundary ve hızlı feedback üzerine kurulmalıdır.
Her Class'ı Unit Test Etmek
Class test hedefi değil implementation unit'i olabilir. Trivial mapper veya data holder için ayrı test düşük değer üretir. Business behavior birden fazla class arasında olabilir. Test coverage class sayısından bağımsız düşünülmelidir. Risk ve davranış test hedefini belirlemelidir.
Private Method Test Etmek
Private method external contract değildir. Doğrudan test etmek refactor'u zorlaştırır. Public behavior üzerinden doğrulanmalıdır. Çok karmaşık private logic ayrı abstraction'a çıkarılabilir. Test domain behavior'a odaklanmalıdır.
Her Dependency'yi Mocklamak
Mock sayısı arttıkça test gerçek sistemden uzaklaşabilir. Internal class'ları gerçek kullanmak çoğu zaman daha sade olur. Boundary dependency'ler stub veya mock için daha iyi adaydır. Database mock yerine integration test gerektirir. Behavior testleri interaction detayından daha değerlidir.
Production PostgreSQL Kullanıp Testte SQLite Kullanmak
Provider ve SQL semantics farklıdır. Query, type ve constraint behavior production'da değişebilir. Testler false confidence sağlayabilir. PostgreSQL container modern ve uygulanabilir bir alternatiftir. Same-engine integration daha güçlü güven verir.
Integration Testleri Shared Database Üzerinde Çalıştırmak
Shared database testler arasında state conflict yaratır. Parallel pipeline birbirini bozabilir. Manual data değişikliği failure reproducibility'yi düşürür. Disposable database veya schema isolation daha güvenlidir. Shared staging ana integration suite olmamalıdır.
Testlerin Birbirine Bağımlı Olması
Test B, Test A'nın bıraktığı data'ya güvenmemelidir. Execution order değişebilir. Parallel mode bu problemi ortaya çıkarır. Her test kendi setup'ını kurmalıdır. Independence debugging ve retry kolaylığı sağlar.
Her Şeyi E2E Test Etmek
E2E pahalı ve yavaştır. Business edge case'leri daha düşük seviyede test etmek verimlidir. E2E kritik journey'leri doğrulamalıdır. Fazla E2E flaky test riskini artırır. Test pyramid veya benzer trade-off modelleri bu dengeyi hatırlatır.
Coverage'ı Tek Kalite Metriği Yapmak
Yüzde hedefi assertion quality'yi göstermez. Developer düşük değerli testlerle sayıyı yükseltebilir. Business risk ve mutation score gibi ek sinyaller gerekir. Coverage boş bölgeleri keşfetmek için faydalıdır. Quality decision tek metriğe bağlanmamalıdır.
Flaky Testleri Sürekli Retry Etmek
Retry problemi gizler. Developer failure'a güvenmemeye başlar. İlk-run failure rate takip edilmelidir. Test owner problemi çözmelidir. Quarantine varsa geçici ve süreli olmalıdır.
Test Kodunun Bakımını İhmal Etmek
Test helper ve fixture zamanla ağır teknik borca dönüşebilir. Production change her seferinde onlarca test kırabilir. Test refactor da planlanmalıdır. Duplicate veya düşük değerli testler kaldırılabilir. Sağlıklı suite küçük iyileştirmelerle korunur.
Dil ve Framework Bazında Backend Test Araçları
Test stratejisinin temel prensipleri programlama dilinden bağımsızdır, ancak kullanılan framework doğru araç seçimini etkiler. Java, .NET, Node.js, Python ve Go ekosistemlerinin güçlü unit ve integration test çözümleri vardır. Araç seçiminde mocking kolaylığından çok test runner hızı, container integration ve HTTP test desteği değerlendirilmelidir. Aynı strateji farklı teknik stack üzerinde uygulanabilir. Asıl hedef production behavior'a uygun test sınırları oluşturmaktır.
Java
Java ekosistemi unit, integration ve container testleri için geniş seçenekler sunar. JUnit temel runner rolünü üstlenebilir. Mockito interaction veya stub ihtiyacında kullanılabilir. Spring Boot Test gerçek application context ve HTTP testleri sağlar. Testcontainers gerçek database ve broker entegrasyonu için güçlü seçeneklerden biridir.
JUnit
JUnit Java unit testlerin temel framework'lerinden biridir. Parameterized test ve lifecycle extension desteği sunar. Test naming ve grouping kolaydır. Integration suite için de runner olarak kullanılabilir. Strategy framework'ten bağımsız kalmalıdır.
Mockito
Mockito stub ve mock oluşturmayı kolaylaştırır. External boundary unit testlerinde faydalıdır. Her internal class için kullanılmamalıdır. Interaction verification ölçülü tutulmalıdır. Behavior-focused assertion öncelikli olmalıdır.
Spring Boot Test
Spring application context gerçek configuration ile test edilebilir. Test web server HTTP request pipeline'ını doğrular. Belirli bean'ler test double ile değiştirilebilir. Database gerçek container'a bağlanabilir. Broad integration testler için kullanışlıdır.
Testcontainers
Java ekosisteminde Testcontainers güçlü ve yaygın kullanım sunar. PostgreSQL, Kafka ve Redis gibi dependency'ler programmatically başlatılabilir. JUnit lifecycle ile entegre olur. Dynamic properties application configuration'a aktarılır. CI ve local setup aynı kalır.
.NET
.NET tarafında xUnit, NUnit ve MSTest farklı test runner seçenekleri sunar. ASP.NET Core uygulamalarında WebApplicationFactory gerçek HTTP pipeline integration testleri için oldukça kullanışlıdır. Testcontainers gerçek database ve infrastructure dependency sağlayabilir. Dependency injection test configuration ile controlled override yapılabilir. Domain logic testleri framework host başlatmadan hızlı biçimde çalıştırılmalıdır.
xUnit
xUnit modern .NET test projelerinde sık kullanılan bir runner'dır. Fixture ve parallel execution desteği vardır. Unit ve integration test project'lerinde kullanılabilir. Naming ve category yaklaşımı ekip standardına göre şekillenebilir. Runner seçimi test strategy'nin kendisi değildir.
NUnit
NUnit güçlü attribute ve parameterized test özellikleri sunar. Existing .NET projelerinde yaygın olabilir. Integration test setup fixture'ları desteklenir. Parallelism dikkatle yapılandırılmalıdır. Test readability temel kriter olmaya devam eder.
MSTest
MSTest Microsoft ekosistemiyle doğal entegrasyon sunar. Unit ve integration senaryolarında kullanılabilir. CI runner desteği geniştir. Test category stage separation için kullanılabilir. Framework tercihi ekip alışkanlığına bağlı olabilir.
WebApplicationFactory
ASP.NET Core application'ı test host üzerinde çalıştırmayı sağlar. Gerçek routing, middleware ve serialization pipeline kullanılır. Authentication veya external service dependency test configuration ile değiştirilebilir. Database Testcontainers üzerinden gerçek engine olabilir. API integration testleri için yüksek değer sağlar.
Testcontainers
.NET Testcontainers package database ve broker lifecycle yönetimini kolaylaştırır. Dynamic connection string test host'a aktarılabilir. Suite startup ve cleanup programmatic hale gelir. Local Docker ve CI aynı behavior'ı sunar. Container reuse veya fixture scope runtime'ı optimize edebilir.
Node.js / TypeScript
Node.js ve TypeScript tarafında Jest veya Vitest unit test runner olarak kullanılabilir. Supertest HTTP endpoint integration için pratik bir araçtır. Testcontainers gerçek PostgreSQL, Redis ve broker dependency başlatabilir. Async testlerde explicit waiting ve cleanup büyük önem taşır. Open handle problemi flaky veya uzun test shutdown'ına yol açabileceği için resource lifecycle doğru yönetilmelidir.
Jest
Jest assertion, mocking ve test runner özelliklerini birlikte sunar. Unit testlerde hızlı setup sağlar. Mock kullanımının sınırı yine behavior strategy ile belirlenmelidir. Async testler promise veya async/await ile açık yazılmalıdır. Integration testler ayrı project configuration kullanabilir.
Vitest
Vitest hızlı test runner ve modern TypeScript tooling ile uyum sunar. Unit test feedback süresi güçlü olabilir. API büyük ölçüde tanıdık bir yapı sunar. Integration suite de çalıştırılabilir. Tool seçiminden çok dependency isolation önemlidir.
Supertest
Supertest HTTP application'ına test request göndermeyi kolaylaştırır. Routing, middleware ve response contract doğrulanabilir. Gerçek network port'u açmak gerekmeyebilir. Database gerçek container olabilir. Controller method'unu doğrudan çağırmaktan daha yüksek pipeline fidelity sağlar.
Testcontainers
Node Testcontainers test sırasında dependency container başlatabilir. PostgreSQL ve Redis integration testleri gerçek semantics kullanır. Async lifecycle doğru await edilmelidir. Parallel suite resource consumption izlenmelidir. CI Docker erişimi gerektirir.
Python
Python projelerinde pytest okunabilir fixture ve parameterization desteğiyle güçlü bir test altyapısı sağlar. Standard unittest mevcut codebase'lerde kullanılabilir. Database integration için Testcontainers veya Docker dependency tercih edilebilir. Django veya FastAPI gibi framework'ler test client özellikleri sunar. Test fixture scope ve transaction behavior framework semantics'e uygun seçilmelidir.
pytest
pytest fixture ve parameterized test kullanımını oldukça sade hale getirir. Marker'lar unit ve integration suite ayrımı sağlayabilir. Failure output okunabilirdir. Plugin ekosistemi geniştir. Fixture aşırı katmanlaşırsa test niyeti gizlenebileceği için sade tutulmalıdır.
unittest
Python standard library içinde gelir ve ek dependency gerektirmez. Class-based test organization sunar. Existing legacy codebase'de yaygın olabilir. Mock library ile birlikte kullanılabilir. Strategy aynı davranış odaklı ilkeleri korur.
Testcontainers
Python Testcontainers gerçek database veya service container başlatmayı sağlar. pytest fixture ile lifecycle yönetilebilir. Connection URL runtime alınır. Migration test öncesinde uygulanır. CI Docker configuration gerekli olur.
Go
Go standard testing package sade ve hızlı test altyapısı sunar. Table-driven tests dil ekosisteminde doğal bir yöntemdir. testify assertion ve helper özellikleri sağlayabilir. httptest HTTP handler veya server integration testlerinde kullanışlıdır. Testcontainers gerçek infrastructure dependency'leri integration suite'e dahil edebilir.
testing
Standard package ek test framework'üne ihtiyaç duymadan unit ve integration test çalıştırır. Benchmark desteği de bulunur. Table-driven style boundary input'lar için kullanışlıdır. Subtest ve parallel execution doğal olarak desteklenir. Dependency isolation geliştiricinin tasarım sorumluluğudur.
testify
testify assertion ve suite helper sunar. Mock özellikleri de kullanılabilir. Internal dependency'leri gereksiz mocklamama prensibi yine geçerlidir. Readability için assertion library yararlı olabilir. Standard testing package ile birlikte çalışır.
httptest
Go HTTP handler'ları gerçek request-response semantics'e yakın test etmeyi sağlar. Local stub external server da oluşturulabilir. Network dependency kontrollü hale gelir. Headers ve status codes doğrulanır. API integration suite için hafif bir araçtır.
Testcontainers
Go Testcontainers gerçek PostgreSQL veya broker instance başlatabilir. Context üzerinden lifecycle yönetilir. Test suite container'ı paylaşabilir. Parallel testlerde ayrı schema veya database gerekebilir. Same-engine integration confidence sağlar.
Örnek Production-Ready Backend Test Dağılımı
Production-ready test dağılımı sabit yüzde yerine component riskine göre kurulmalıdır. Domain logic yoğun unit testlerle korunabilir. Repository gerçek database integration testlerinden geçer. HTTP API endpoint'leri representative integration testlerle doğrulanır, external API boundary contract ve stub server üzerinden test edilir. Kritik kullanıcı journey'leri için az sayıda E2E test yeterli olabilir.
Domain Logic
Domain logic çoğunlukla saf ve hızlıdır. Edge case sayısı burada yüksektir. Parameterized ve property-based testing kullanılabilir. External dependency minimum tutulur. Failure business rule'a doğrudan işaret eder.
Yoğun Unit Tests
Her önemli business rule hem success hem failure path ile korunmalıdır. Testler milisaniyeler içinde çalışır. Mock yerine gerçek domain object'ler tercih edilebilir. Clock veya gateway gibi boundary'ler controlled dependency olur. Coverage burada daha yüksek olabilir.
Database Repository
Repository'nin gerçek SQL ve ORM davranışı integration testte doğrulanır. Fake repository ana güven kaynağı olmamalıdır. Constraint ve transaction testleri eklenir. Migration aynı test environment üzerinde uygulanır. Containerized PostgreSQL kullanılabilir.
Gerçek Database Integration Tests
Production ile aynı engine'e yakın environment kullanılır. Test data factory minimum fixture üretir. Query sonuçları ve error semantics doğrulanır. Parallel isolation sağlanır. Database version upgrade regression suite ile test edilir.
HTTP API
API test server gerçek routing ve middleware'i çalıştırır. Authentication ve serialization test edilir. Business permutation domain unit testte kalır. Representative endpoint cases yeterlidir. Error contract da doğrulanmalıdır.
Endpoint Integration Tests
Gerçek HTTP request gönderilir. Database gerekiyorsa container kullanılır. External service controlled stub olabilir. Response status ve body contract kontrol edilir. Test pipeline behavior'ına odaklanır.
External API
External client local stub server ile test edilir. Request contract ve parsing doğrulanır. Timeout ve 429 gibi failure cases eklenir. Provider contract ayrıca consumer-driven testle korunabilir. Gerçek sandbox yalnız smoke amaçlı kullanılabilir.
Contract + Stub Integration Tests
Contract provider değişikliklerini erken gösterir. Stub integration sizin HTTP client behavior'ınızı doğrular. İki yaklaşım birlikte güçlü coverage sağlar. E2E external provider dependence azalır. Failure localization kolaylaşır.
Queue/Event
Broker integration publish-consume davranışını doğrular. Event schema contract test edilir. Duplicate ve ordering riskleri ayrıca ele alınır. Consumer idempotency gerçek database ile doğrulanır. Eventual consistency explicit waiting kullanır.
Broker Integration + Contract Tests
Real broker container delivery semantics sağlar. Contract schema compatibility'yi korur. Her business rule broker üzerinden tekrar edilmez. Critical consumer path representative eventlerle test edilir. DLQ ve retry configuration ayrıca doğrulanabilir.
Kritik Kullanıcı Akışları
Kullanıcı açısından en değerli journey'ler E2E olabilir. Login, ödeme veya sipariş creation buna örnek verilebilir. Test sayısı düşük tutulur. Environment güvenilir olmalıdır. Failure release kararını etkileyebilir.
Az Sayıda E2E Test
E2E suite birkaç kritik flow'a odaklanır. Unit ve integration suite'in tekrarını yapmaz. Runtime ve flakiness kontrol altında kalır. Deployment smoke subset oluşturulabilir. Böylece en pahalı test katmanı ölçülü kullanılır.
Örnek Backend Test Akışı
Yeni bir backend özelliği geliştirirken test akışını requirement'tan başlayarak kurmak faydalıdır. Önce domain edge case'leri unit testlerle ifade edilir. Sonra gerçek database ve HTTP boundary integration testlerle doğrulanır. External contract'lar ve kritik E2E journey gerektiği ölçüde eklenir. Tüm testler CI quality gate içinde doğru hız katmanına yerleştirilir.
1. Business Requirement Tanımlanır
Feature'ın ne yapması gerektiği business dilinde yazılır. Success ve failure davranışları belirlenir. Security ve idempotency riskleri çıkarılır. Hangi external boundary'lerin etkilendiği listelenir. Test planı bu requirement'tan türetilir.
2. Domain Edge Case'leri Unit Testlere Yazılır
Calculation ve validation detayları hızlı testlere dönüştürülür. Boundary values ve invalid states eklenir. Gerekirse parameterized veya property-based test kullanılır. Testler infrastructure'dan bağımsız çalışır. Feature geliştirme sırasında hızlı feedback sağlar.
3. Database Boundary Gerçek Dependency ile Test Edilir
Repository query ve constraint PostgreSQL container üzerinde çalışır. Migration yeni schema'yı oluşturur. Transaction behavior test edilir. Fake repository ile yetinilmez. Production SQL semantics erkenden doğrulanır.
4. API Request–Response Pipeline Test Edilir
Test server üzerinden gerçek HTTP request gönderilir. Routing, validation, authentication ve serialization çalışır. Business edge case'lerin tamamı tekrar edilmez. Representative success ve error scenarios seçilir. Response contract korunur.
5. External Contract'lar Doğrulanır
Harici servis client request ve parsing local stub üzerinden test edilir. Consumer/provider contract varsa pipeline'da doğrulanır. Timeout ve error mapping eklenir. Sandbox gerekirse ayrı smoke stage kullanır. External availability main test suite'i bloklamaz.
6. Kritik Journey için E2E Test Eklenir
Feature kullanıcı açısından kritikse tam akış test edilir. Journey mümkün olduğunca kısa tutulur. Alt seviyelerde zaten test edilen permutations tekrar edilmez. Environment stable olmalıdır. Failure release için önemli sinyal sağlar.
7. Tüm Testler CI Quality Gate'e Bağlanır
Fast tests önce çalışır. Integration suite paralel stage'e alınabilir. Contract compatibility kontrol edilir. Changed-code coverage signal olarak kullanılır. Critical failure merge'i engeller.
8. Production Regression'ları Yeni Testlere Dönüştürülür
Production incident sonrası reproduce test yazılır. Eksik test katmanı analiz edilir. Fix sonrasında test kalıcı suite'e eklenir. Incident pattern test strategy review'a girer. Sistem gerçek kullanım üzerinden sürekli daha güvenilir hale gelir.
Backend Test Maturity Model
Backend test maturity test sayısından çok testlerin ne kadar güvenilir, otomatik ve risk odaklı olduğuyla ölçülebilir. Manuel kontrolden başlayan ekip zamanla unit ve API integration seviyesine geçebilir. Gerçek database, queue ve ephemeral infrastructure daha yüksek production fidelity sağlar. Contract testing ve flaky test governance dağıtık sistemlerde olgunluğu artırır. Son aşamada ekip test portföyünü sabit oran yerine feedback ve risk verileriyle düzenli olarak yönetir.
Seviye 0 — Manuel Test
Feature çoğunlukla developer veya QA tarafından elle kontrol edilir. Regression tekrar doğrulaması pahalıdır. Refactor güveni düşüktür. Production bug aynı şekilde tekrar edebilir. Otomatik test için en kritik business flow'lardan başlanmalıdır.
Seviye 1 — Temel Unit Tests
Business logic için hızlı testler eklenmiştir. Developer local feedback almaya başlar. External boundary'ler büyük ölçüde mock olabilir. Database gerçek behavior henüz zayıf doğrulanır. Bir sonraki adım integration layer eklemektir.
Seviye 2 — Unit + API Integration Tests
HTTP pipeline için gerçek endpoint testleri bulunur. Authentication ve serialization daha güvenilir hale gelir. Database bazı testlerde gerçek olabilir. CI merge request suite otomatik çalışır. Test duplication henüz gözden geçirilmelidir.
Seviye 3 — Gerçek Database/Queue Integration Tests
Production database engine container ile test edilir. Message broker semantics integration suite'e eklenir. Constraint ve migration testleri vardır. Shared staging bağımlılığı azalır. Backend boundary confidence ciddi biçimde yükselir.
Seviye 4 — Contract Testing + Ephemeral Infrastructure
Servisler arası contract otomatik doğrulanır. Test dependency'leri disposable environment'da çalışır. Local ve CI setup uyumludur. E2E sayısı optimize edilebilir. Distributed deployment güveni artar.
Seviye 5 — CI Quality Gates + Flaky Test Governance
Critical suite merge'i bloklar. Flaky test oranı ve owner'ları görünürdür. Changed-code coverage ve security tests quality gate'e dahildir. Runtime budget izlenir. Test reliability engineering metriği haline gelir.
Seviye 6 — Risk ve Feedback Odaklı Test Portföyü
Ekip test sayılarını değil sağlanan güveni optimize eder. Production incident ve test timing verileri strategy'yi besler. Mutation testing veya property-based testing riskli modüllerde kullanılır. Test dağılımı architecture değiştikçe güncellenir. Bu seviye backend test stratejisinin yaşayan bir engineering practice olduğunu kabul eder.
Production'a Çıkmadan Önce Backend Test Kontrol Listesi
Production öncesi kontrol listesi test sayısını değil kritik risklerin gerçekten doğrulanıp doğrulanmadığını değerlendirmelidir. Business rules, error path, database, migration, authorization ve external failure senaryoları temel alanlardır. Integration testlerin isolated ve mümkünse parallel çalışması CI süresini yönetilebilir tutar. Flaky testler release güvenini düşürdüğü için çözülmelidir. Kritik production journey için az sayıda smoke veya E2E test bulunması deployment confidence sağlar.
Kritik Business Rules Unit Test Ediliyor mu?
Para, yetki veya state transition gibi high-impact rules hızlı unit testlerle korunmalıdır. Edge case'ler açıkça listelenmelidir. Testler implementation detail'e bağımlı olmamalıdır. Failure business rule'u göstermelidir. Production incident history coverage kararına dahil edilmelidir.
Error Path'ler Test Edildi mi?
Yalnız success behavior yeterli değildir. Invalid input, dependency failure ve timeout scenarios test edilmelidir. Error mapping API contract'la uyumlu olmalıdır. Sensitive detail response'a sızmamalıdır. Retry yalnız uygun error sınıflarında devreye girmelidir.
Database Gerçek Engine ile Test Ediliyor mu?
Production PostgreSQL ise integration suite mümkün olduğunca PostgreSQL kullanmalıdır. Query, constraint ve transaction behavior gerçek engine üzerinde doğrulanmalıdır. Fake repository tek confidence kaynağı olmamalıdır. Container startup CI'da otomatik olmalıdır. Engine version upgrade regression tests gerektirir.
Migration Testleri Var mı?
Empty ve previous-version database upgrade test edilmelidir. Existing data senaryosu gerekiyorsa fixture oluşturulmalıdır. Constraint ve index creation doğrulanabilir. Destructive migration planlı olmalıdır. Failure durumunda roll-forward veya restore yaklaşımı bilinmelidir.
Authentication ve Authorization Test Edildi mi?
Anonymous ve invalid token scenarios bulunmalıdır. Role ve ownership rules endpoint üzerinden test edilmelidir. 401 ve 403 doğru ayrılmalıdır. New endpoint accidental public access'e karşı korunmalıdır. Critical security tests merge gate olabilir.
External API Failure Senaryoları Var mı?
Timeout, 429 ve 503 gibi normal failure mode'lar test edilmelidir. Client retry ve circuit behavior doğrulanır. Invalid JSON parsing error'a dönüşmelidir. Local stub deterministic ortam sağlar. Provider sandbox yalnız ek smoke confidence sunar.
Queue/Event Contract'ları Test Ediliyor mu?
Event schema ve consumer compatibility korunmalıdır. Real broker publish-consume testleri çalışabilir. Duplicate message idempotency doğrulanır. DLQ ve retry configuration test edilir. Eventual consistency explicit waiting kullanır.
Idempotency Test Edildi mi?
Aynı request veya event iki kez çalıştırılmalıdır. Side effect sayısı bir kalmalıdır. Concurrent duplicate scenario kritik sistemlerde test edilmelidir. Database unique constraint gibi atomic guard kullanılabilir. Payment ve webhook endpoint'leri yüksek önceliğe sahiptir.
Integration Testler İzole mi?
Test execution order sonuç değiştirmemelidir. Shared mutable database state kullanılmamalıdır. Cleanup veya schema isolation planı bulunmalıdır. Disposable containers suite lifecycle'ını sadeleştirir. Random state failure reproducibility'yi bozmamalıdır.
Testler Parallel Çalışabiliyor mu?
Parallel execution pipeline runtime'ı ciddi biçimde azaltabilir. Test data collision çözülmüş olmalıdır. Worker-specific schema veya container kullanılabilir. Global singleton state olmamalıdır. Test runner timing düzenli izlenmelidir.
Flaky Test Var mı?
First-run failure metric'i gözden geçirilmelidir. Retry arkasına saklanan testler belirlenmelidir. Quarantine geçici olmalıdır. Owner ve fix deadline bulunmalıdır. Quality gate yalnız güvenilir testlerden oluşmalıdır.
Merge Öncesi Testler Otomatik Çalışıyor mu?
Developer'ın test çalıştırmayı hatırlamasına güvenilmemelidir. CI merge request üzerinde critical suite'i otomatik çalıştırmalıdır. Fast tests önce feedback vermelidir. Failure merge'i policy'ye göre bloklamalıdır. Manual override audit edilebilir olmalıdır.
Kritik Production Flow için E2E/Smoke Test Var mı?
En önemli kullanıcı journey deployment sonrası doğrulanmalıdır. Test sayısı az ve stable olmalıdır. Full edge case suite E2E'ye taşınmamalıdır. Smoke test hızlı fail etmelidir. Monitoring ile birlikte production readiness sinyali sağlar.
Sık Sorulan Sorular
Backend Test Stratejileri: Birim (Unit) ve Entegrasyon Testleri konusunda sık sorulan sorular genellikle hangi davranışın hangi test seviyesinde bulunması gerektiği etrafında toplanır. Unit ve integration test birbirinin alternatifi değildir ve farklı riskleri doğrular. Mocking, coverage veya test pyramid gibi araçlar da tek başına test kalitesini belirlemez. Aşağıdaki cevaplar production odaklı backend test portföyü kurmak isteyen ekipler için kısa bir karar çerçevesi sunar. Kurumsal backend test otomasyonu ve yazılım kalite güvence hizmeti planlanırken de aynı prensipler temel alınabilir.
Unit test nedir?
Unit test küçük ve anlamlı bir davranış birimini hızlı biçimde doğrular. External I/O mümkün olduğunca sınır dışında tutulur. Unit her zaman tek class olmak zorunda değildir. Business logic ve edge case'ler için çok uygundur. Test deterministic ve kolay teşhis edilebilir olmalıdır.
Integration test nedir?
Integration test component'lerin gerçek boundary üzerinden birlikte çalışmasını doğrular. Database, HTTP, queue veya cache sık kullanılan örneklerdir. Unit testten daha yavaştır fakat production'a daha yakın davranış gösterir. Gerçek dependency semantics'i önemli olduğunda integration test tercih edilmelidir. Test setup isolated ve otomatik olmalıdır.
Unit test ile integration test arasındaki fark nedir?
Unit test local davranışı hızlı ve kontrollü ortamda doğrular. Integration test gerçek component sınırını ve infrastructure davranışını test eder. Unit test daha ucuz olduğu için birçok business permutation burada bulunabilir. Integration test query, serialization veya middleware gibi riskleri yakalar. Sağlıklı backend test strategy iki seviyeyi birlikte kullanır.
Backend'de hangi kodlar unit test edilmelidir?
Business rules, calculations, validation ve state transition iyi unit test adaylarıdır. External dependency gerektirmeyen deterministic logic önceliklidir. Framework internals veya trivial getter'ları test etmek zorunlu değildir. Implementation detail yerine observable behavior hedeflenmelidir. Critical edge case'ler unit suite'te ayrıntılı yer alabilir.
Controller unit test edilmeli mi?
Controller yalnız HTTP mapping yapıyorsa çoğu davranış API integration testle daha anlamlı doğrulanabilir. Business logic controller içindeyse unit test gerekebilir fakat logic'i service veya domain'e taşımak daha iyi tasarım olabilir. Route ve middleware controller unit testte çalışmaz. Response contract gerçek HTTP üzerinden görülmelidir. Duplication'dan kaçınmak önemlidir.
Repository unit test edilmeli mi?
Repository'nin temel değeri database interaction'dır. Bu nedenle mock database ile repository unit test çoğu zaman sınırlı güven sağlar. Real database integration test daha uygun olabilir. Query, mapping ve constraint behavior gerçek engine'de görülür. Repository içindeki saf transformation logic varsa ayrı unit test edilebilir.
Database integration test nasıl yapılır?
Production'a yakın database container başlatılır. Migration uygulanır ve minimum fixture hazırlanır. Repository veya API behavior çalıştırılır. Sonuç ve database state doğrulanır. Test sonunda state cleanup veya container disposal uygulanır.
Testlerde gerçek database kullanılmalı mı?
Database behavior'ını test ediyorsanız evet, gerçek engine kullanmak güçlü confidence sağlar. Her unit test için database gerekmez. Integration suite production engine'e yakın dependency kullanmalıdır. Testcontainers setup maliyetini azaltır. Fake database hızlı logic tests için sınırlı rol oynayabilir.
PostgreSQL kullanırken testte SQLite kullanmak doğru mu?
Basit bazı testlerde mümkün olsa da repository integration confidence için risklidir. SQL syntax, type, JSON ve transaction semantics farklıdır. ORM provider farklı query üretebilir. PostgreSQL container kullanmak daha güvenilir sonuç sağlar. Özellikle database-specific feature kullanan projelerde same-engine test tercih edilmelidir.
Testcontainers nedir?
Test sırasında disposable database ve diğer infrastructure dependency'leri başlatmayı kolaylaştıran bir test yaklaşımı ve araç ekosistemidir. Gerçek PostgreSQL, Redis veya message broker kullanılabilir. Local ve CI aynı setup'ı paylaşabilir. Test sonrasında resource'lar otomatik silinebilir. Shared test environment problemlerini önemli ölçüde azaltır.
Mock ile stub arasındaki fark nedir?
Stub sisteme controlled input sağlar. Mock çoğunlukla belirli interaction expectation'ını doğrular. Stub kullanan test state veya return result assert etmeye odaklanır. Mock dış side effect interaction önemliyse değerlidir. Her dependency'yi mocklamak gerekli değildir.
Her dependency mocklanmalı mı?
Hayır, özellikle internal domain object'leri gerçek kullanmak çoğu zaman daha sağlıklıdır. External boundary, clock veya nondeterministic dependency mock veya stub için iyi adaydır. Database semantics mocklanmamalıdır. Mock-heavy suite refactor sırasında gereksiz kırılabilir. Test double seçiminde behavior ve fidelity dikkate alınmalıdır.
Test pyramid nedir?
Test Pyramid hızlı unit testlerin geniş taban, integration testlerin orta katman ve az E2E testin üst katman olduğu bir modeldir. Mutlak test oranı değildir. Amaç hızlı feedback ve production confidence arasında denge kurmaktır. Modern projelerde integration katmanı daha güçlü olabilir. Risk ve architecture oranlardan daha önemlidir.
Testlerin yüzde kaçı unit test olmalıdır?
Evrensel bir yüzde bulunmaz. Domain-heavy projede unit oranı yüksek olabilir. Data-heavy backend daha fazla database integration testine ihtiyaç duyabilir. Microservice contract testlere yatırım yapabilir. Test dağılımı risk, speed ve fidelity üzerinden belirlenmelidir.
Contract testing nedir?
Contract testing consumer ile provider arasındaki API veya message beklentisini doğrular. Breaking değişiklikler full E2E environment gerekmeden yakalanabilir. Consumer-driven contract özellikle microservice sistemlerde kullanışlıdır. Provider kendi pipeline'ında contract verification çalıştırır. Integration ve E2E testleri tamamlayan bir katmandır.
Integration test ile E2E test arasındaki fark nedir?
Integration test belirli component boundary'sine odaklanabilir. E2E sistemin büyük bölümünü veya kullanıcı journey'sini birlikte çalıştırır. Integration daha hızlı ve failure localization açısından daha kolaydır. E2E daha yüksek environment maliyeti taşır. Bu nedenle E2E kritik akışlarla sınırlı tutulmalıdır.
Code coverage yüzde kaç olmalıdır?
Tek doğru yüzde bulunmaz. Coverage riskli modüllerde testsiz alanları göstermek için kullanılabilir. Changed-code coverage büyük legacy projelerde faydalıdır. Yüzde yüksekliği assertion quality'yi garanti etmez. Risk coverage ve mutation testing gibi sinyallerle birlikte değerlendirilmelidir.
%100 coverage gerekli midir?
Hayır, yüzde yüz coverage düşük değerli testler yazmaya yol açabilir. Trivial code'u test etmek önemli business riskini korumaz. Critical behavior güçlü test edilmelidir. Coverage gap değerlendirmesi bağlamla yapılmalıdır. Quality hedefi güven olmalıdır.
Flaky test nedir?
Aynı kod üzerinde bazen geçen bazen kalan test flaky olarak adlandırılır. Shared state, time, network veya async race başlıca nedenlerdir. Retry problemi gizleyebilir. Test owner sorunu çözmelidir. Disposable infrastructure ve deterministic input flakiness'i azaltır.
Integration testler CI/CD'de nasıl çalıştırılmalıdır?
Fast integration suite merge request pipeline'da çalışabilir. Daha ağır suite parallel stage veya main branch pipeline'a alınabilir. Dependency container'lar otomatik başlatılmalıdır. Test environment isolated olmalıdır. Failure release veya merge policy'ye göre quality gate oluşturabilir.
Production bug'ı için hangi test türü yazılmalıdır?
Bug'ın root cause'una en yakın test seviyesi seçilmelidir. Calculation bug unit test olabilir. SQL veya constraint bug database integration test gerektirir. Service contract bug contract test olabilir. Önce bug'ı fail eden test yazılmalıdır. Sonra fix regression test ile kalıcı olarak korunmalıdır.
Backend Test Stratejileri Hakkında Ek Sorular
Backend Test Stratejileri: Birim (Unit) ve Entegrasyon Testleri planlanırken ekipler çoğu zaman test araçlarından önce hangi riskin hangi seviyede doğrulanması gerektiğine karar vermelidir. Backend unit ve integration testleri nasıl yazılır sorusu tek bir framework cevabına indirgenemez. Database ve HTTP gibi gerçek sistem sınırları integration testle, yoğun business kuralları ise çoğunlukla hızlı unit testlerle korunur. CI/CD tarafında feedback süresi, test reliability ve changed-code coverage birlikte değerlendirilmelidir. Kurumsal backend test otomasyonu ve yazılım kalite güvence hizmeti ihtiyacında da mevcut test portföyünün bu risk alanları üzerinden incelenmesi daha sağlıklı bir başlangıç oluşturur.
Backend test stratejilerinde birim (Unit) ve entegrasyon testleri nasıl planlanmalıdır?
Önce business riskleri ve infrastructure boundary'leri çıkarılmalıdır. Calculation, validation ve state transition gibi davranışlar unit testlere yerleştirilebilir. Database, HTTP, queue ve cache gibi sınırlar gerçek dependency integration testleriyle doğrulanmalıdır. E2E yalnız kritik kullanıcı journey'lerinde kullanılmalıdır. Test dağılımı sabit yüzde yerine hız, reliability ve production fidelity üzerinden düzenlenmelidir.
Unit Test ile Integration Test arasındaki farklar nelerdir ve hangi durumlarda hangisi kullanılmalıdır?
Unit test hızlı ve local davranışı doğrular. Integration test birden fazla component'in gerçek boundary üzerinden birlikte çalışmasını test eder. Business rule ve edge case için unit test daha ucuzdur. ORM query veya authentication pipeline gibi gerçek sistem davranışı için integration test daha uygundur. Aynı senaryoyu her iki seviyede ayrıntılı tekrar etmek yerine her testin farklı risk sorusunu cevaplaması gerekir.
Backend uygulamalarında veritabanı, API ve harici servis entegrasyonları nasıl test edilmelidir?
Database mümkün olduğunca production ile aynı engine üzerinde disposable container ile test edilmelidir. API testleri gerçek HTTP request pipeline'ını çalıştırmalıdır. Harici servis client'ları local HTTP stub veya contract test yaklaşımıyla doğrulanabilir. Timeout, 429 ve invalid response gibi failure senaryoları özellikle eklenmelidir. Shared staging environment yerine izole ve tekrar kurulabilir test dependency'leri daha güvenilir sonuç verir.
Unit ve entegrasyon testleri CI/CD süreçlerine nasıl entegre edilmeli ve test kapsamı (Code Coverage) nasıl yönetilmelidir?
Unit testler ve static checks pipeline'ın erken stage'lerinde çalışmalıdır. Fast integration tests merge request sırasında, ağır integration veya E2E suite daha sonraki stage'lerde çalıştırılabilir. Coverage tek kalite metriği olarak kullanılmamalıdır. Changed-code coverage yeni değişiklikler için faydalı bir sinyal sunabilir. Flaky test oranı ve toplam feedback süresi de test sağlığının temel metrikleri arasında bulunmalıdır.
Backend birim ve entegrasyon testleri konusunda yakınımda danışmanlık veya eğitim nerede bulabilirim?
Backend test otomasyonu ve QA danışmanlığı yakınımda şeklinde araştırma yapan geliştiriciler, teknik bilgiyi doğrudan proje üzerinde uygulayabilecekleri topluluk çalışmalarından faydalanabilir. Diyarbakır Yazılım Topluluğu hakkında daha fazla bilgi için https://www.diyarbakiryazilim.com.tr/about adresi incelenebilir. Proje odaklı çalışmalar için https://www.diyarbakiryazilim.com.tr/projects sayfası kullanılabilir. Özellikle gerçek database integration testleri, API testleri, CI quality gate ve Testcontainers gibi konuları küçük bir backend projesinde uygulamak öğrenme sürecini hızlandırır. Ekipler açısından da mevcut test suite'in hız, reliability, coverage ve production boundary'leri üzerinden değerlendirilmesi iyi bir başlangıç noktasıdır.
Sonuç: Etkili Bir Backend Test Stratejisi Nasıl Kurulur?
Etkili Backend Test Stratejileri: Birim (Unit) ve Entegrasyon Testleri yaklaşımı test sayısını artırmaya değil production'a güvenli değişiklik çıkarabilmeye odaklanmalıdır. Business logic hızlı unit testlerle ayrıntılı korunurken database, HTTP, queue ve external client gibi gerçek sınırlar integration testlerle doğrulanmalıdır. Testcontainers ve disposable infrastructure test ortamlarını production'a yaklaştırırken shared state sorunlarını azaltabilir. Contract tests servisler arası breaking değişiklikleri E2E'den daha erken yakalayabilir. İyi bir test strategy coverage yüzdesinden çok feedback süresi, determinism, risk coverage ve hata yakalama gücü üzerinden değerlendirilmelidir.
Test Sayısını Değil Güven Seviyesini Optimize Edin
Binlerce test production confidence garantisi değildir. Her test hangi riski koruduğunu açıkça göstermelidir. Düşük değerli duplicate testler kaldırılabilir. Critical business behavior daha güçlü test katmanlarıyla korunabilir. Test portfolio düzenli olarak production incident verisiyle gözden geçirilmelidir.
Business Logic'i Hızlı Unit Testlerle Koruyun
Domain rules ve edge case'ler unit testlerin en güçlü alanıdır. External dependency minimumda tutulmalıdır. Behavior implementation detail'den daha önemlidir. Parameterized ve property-based testing coverage'ı genişletebilir. Bu hızlı taban developer refactor güvenini artırır.
Gerçek Sistem Sınırlarını Integration Testlerle Doğrulayın
Database query, API pipeline ve queue semantics mocklarla tamamen doğrulanamaz. Gerçek dependency kullanmak production riskini daha erken görünür hale getirir. Narrow integration tests failure localization sağlar. Broad integration yalnız gerekli kritik wiring için kullanılmalıdır. Test data isolation sürecin zorunlu parçasıdır.
Production Dependency'lerinden Gereksiz Fake'lerle Uzaklaşmayın
Fake repository hızlı görünür fakat gerçek database constraint ve transaction davranışını saklayabilir. Production PostgreSQL kullanıyorsanız integration suite'te PostgreSQL kullanmak daha güçlü güven sağlar. Fake yalnız açıkça bilinen sınırlı amaçlar için kullanılmalıdır. External boundary stub ise real contract tests ile desteklenmelidir. Test fidelity bilinçli karar olmalıdır.
Testcontainers ile Gerçeğe Yakın ve İzole Ortamlar Kurun
Disposable database, Redis ve broker setup local ile CI behavior'ını birbirine yaklaştırır. Shared staging dependency azalır. Container version kontrollü kalır. Test sonrası state temizlenir. Bu yaklaşım API veritabanı ve servis entegrasyon testleri nasıl yapılır sorusuna pratik ve ölçeklenebilir bir cevap sunar.
Contract Testleriyle Servisler Arası Uyumluluğu Koruyun
Microservice sistemde full E2E test her breaking change'i en verimli biçimde yakalayamaz. Consumer-driven contract daha hızlı ve lokal feedback sağlar. Provider hangi consumer'ın etkileneceğini görebilir. API ve message schema version kontrollü hale gelir. E2E critical flow sayısı azaltılabilir.
E2E Testleri Kritik Akışlarla Sınırlayın
E2E bütün sistemi birlikte test ettiği için yüksek maintenance maliyeti taşır. Ana kullanıcı journey'leri için güçlü değer sağlar. Business permutations alt test seviyelerinde tutulmalıdır. Suite küçük kalırsa flakiness daha kolay yönetilir. Deployment smoke tests ayrıca hızlı subset olarak kullanılabilir.
Coverage Yerine Risk, Reliability ve Feedback Süresine Odaklanın
Coverage faydalı bir signal fakat kalite hedefinin kendisi değildir. Critical behavior ve security risk coverage daha önemlidir. First-run test reliability suite'e olan güveni belirler. Feedback süresi geliştirici iteration hızını etkiler. Bu metrikleri birlikte değerlendirmek daha gerçekçi kalite resmi sunar.
Flaky Testleri Teknik Borç Olarak Yönetin
Flaky testlerin sürekli retry edilmesi kalite problemini saklar. Test owner ve fix süresi tanımlanmalıdır. Shared state, time ve network root cause olarak incelenmelidir. Quarantine yalnız geçici çözüm olmalıdır. Güvenilir test suite release sürecinin temelidir.
Test Stratejisini Tek Bir Piramit Oranına Değil Sistemin Risk ve Mimarisine Göre Tasarlayın
Her backend uygulaması aynı test dağılımına ihtiyaç duymaz. Domain-heavy sistem daha fazla unit test, data-heavy sistem daha fazla real database integration test kullanabilir. Event-driven veya microservice architecture contract ve broker testlerine daha çok ihtiyaç duyabilir. Bu nedenle Backend Test Stratejileri: Birim (Unit) ve Entegrasyon Testleri yalnız bir piramit çizmek değil, sistemin failure mode'larını doğru test seviyeleriyle eşleştirme işidir. Kendi backend test altyapınızı geliştirmek, teknik çalışma gruplarına katılmak veya proje odaklı işbirliği fırsatlarını değerlendirmek için https://www.diyarbakiryazilim.com.tr adresinden Diyarbakır Yazılım Topluluğu'na ulaşabilirsiniz.
share: