Birim testlerde dis bağımlılıkları kontrol altina almak icin test double kavrami kullanılır. Gerard Meszaros'un terminolojisinde mock, stub, fake, spy ve dummy farklı amaclara hizmet eder; hepsini "mock" diye adlandirmak yanlış arac secimine ve kirilgan testlere yol acar. Bu makalede her test double turunun davranisini, .NET ve JavaScript ekosistemlerindeki karsiliklarini ve karar matrisini detayli inceliyoruz.
Test double spektrumu
Test double, production bagimiliginin yerine gecen her seyi kapsayan genel terimdir. Alt turler etkileşim mi durum mu test edildigine gore ayrilir. Mock ve spy etkilesimi dogrular; stub ve fake çıktı veya durum sağlar; dummy yalnizca imza doldurur.
Dummy
Parametre listesini doldurmak icin verilir; test mantiginda kullanılmaz. Örnek: logger parametresi zorunlu ama test edilen metod log yazmiyorsa NullLogger veya bos bir dummy nesne gecilir. Dummy seçimi performans ve okunabilirlik icindir; davranis assert edilmez.
Stub
Onceden tanimli cevaplar doner. When X then return Y mantigi vardir. Çağrı sayisi veya argumanlar önemli degilse stub yeterlidir. Örnek: IExchangeRateProvider stub'i her zaman 34.5 kur dondurur; fiyat hesaplama testi buna gore yapilir.
var stub = new StubEmailSender();stub.EnqueueSuccess();var service = new UserService(stub);await service.RegisterAsync(user);// Stub yalnizca basarili gonderim simule eder; kac cagri oldugu onemli degilFake
Çalışan ama basitlestirilmis implementasyondur. In-memory repository, embedded SQLite, MailHog yerine bellek icinde mail listesi tutan fake sender. Gerçek davranisa yakın olduğu icin birkac birim testten ote entegrasyona kayabilir; yine de dis ag gerektirmez.
Fake uzun vadede bakimi kolaydir cunku gerçek arayüzü implement eder. InMemoryUserRepository EF Core InMemory provider veya kendi Dictionary tabanli implementasyon olabilir. Dikkat: InMemory provider bazi SQL semantiklerini taklit etmez; kritik sorgular Testcontainers ile test edilmeli.
Spy
Gerçek veya kismi gerçek implementasyonu sarar ve cagrilari kaydeder. Test sonunda Verify ile kac kez cagrildigi kontrol edilir ama mock kadar katı beklenti tanimlamaz. Örnek: Cache'e yazilan degerleri kaydeden spy decorator.
Mock
Beklenen etkilesimi onceden tanimlar ve dogrular. "SendEmail bir kez cagrilmali, arguman su olmali" gibi. Moq, NSubstitute, jest.mock bu kategoridedir. Asiri mock kullanımı refactor direnci yaratir; implementasyon degisince test kirilir though davranis ayni kaldi.
var mock = new Mock<INotificationService>();mock.Setup(n => n.SendAsync(It.Is<Message>(m => m.Subject == "Welcome"))).Returns(Task.CompletedTask);var sut = new OnboardingService(mock.Object);await sut.CompleteAsync(userId);mock.Verify(n => n.SendAsync(It.IsAny<Message>()), Times.Once);Karar matrisi
- Çıktı yeterli, etkileşim onemsiz: Stub
- Gercekci ama hafif depolama: Fake
- Çağrı kaydi gerekli, gevsek dogrulama: Spy
- Tam etkileşim sozlesmesi kritik: Mock
- Parametre doldurma: Dummy
Mock anti-kaliplari
Mock everything: Test edilen sınıf dışında her sey mock ise test yalnizca setup'i dogrular, gerçek entegrasyon hic test edilmez.
Implementation coupling: Private metod çağrısı mock'lanir; public API degismeden test kirilir.
Assertion yok: Mock setup var ama Verify yok; test her zaman yesil.
Tercih sırası genelde: gerçek nesne (mumkunse) > fake > stub > spy > mock. Mock son care olarak etkileşim kritik domainlerde kullanılır.
Dil ve framework notlari
.NET'te Moq ve NSubstitute yaygindir. jest'te fn() stub, jest.spyOn spy, mock module tam mock sağlar. Python'da unittest.mock MagicMock cok amacli ama stub ve mock ayrimi yine dusunulmeli. Go'da interface ve manuel fake struct'lar tercih edilir; mock code generation (mockery) büyük arayuzlerde kullanılır.
Port ve adapter ile test edilebilirlik
Hexagonal mimari test double secimini kolaylastirir. Dis dünya port uzerinden baglanir; testte fake adapter takilir. Domain mantigi infrastructure'dan izole kalir. Port tasarımı dar tutulursa fake implementasyonu küçük kalir.
Async ve zaman bağımlılıkları
IClock veya TimeProvider enjekte edilerek stub zaman sağlanır. Task.Delay yerine kontrollu scheduler kullanılır. Async mock setup'larinda ReturnsAsync ve Callback doğru kullanilmazsa deadlock veya false positive olusur.
Örnek senaryo: siparis servisi
- Stok kontrolu: IInventory stub — yeterli stok var/yok senaryolari.
- Odeme: Mock — Charge cagrildi mi, tutar doğru mu?
- Siparis deposu: Fake in-memory repo — kayıt ve okuma gercekci.
- Metrik: Spy — OrderCreated event sayildi mi?
- Logger: Dummy veya NullObject.
Contract test ile birlikte kullanım
Mock dis servisi simule eder; Pact gibi contract test gerçek saglayici ile tuketici uyumunu garanti eder. Mock yanlış sözleşme tanimlarsa contract test pipeline'da patlar. Iki katman birlikte guven sağlar.
Bakım ve refactoring
Mock kullanan testler API imzasi degisince toplu güncelleme gerektirir. Fake ve stub genelde daha az kirilgandir. Test double secimini code review'da tartisin. "Bu neden mock?" sorusu zayif tasarım sinyali olabilir — belki arayüz cok genistir veya SRP ihlali vardir.
Performans
Mock framework reflection kullanır; binlerce testte fark ihmal edilebilir. Agir fake (büyük in-memory graph) yavaslatir. Test double basit tutulmali; senaryo basina yalnizca gerekli durum hazirlanmali.
Testability ve tasarım geri bildirimi
Test double kullanımı arttikca arayüz tasarımı sorgulanmali. Bes parametreli constructor veya on bağımlılık mock gerektiren sınıf SRP ihlali isaret edebilir. Facade veya application service katmanı test edilebilir yuzeyi daraltir. Test double seçimi yalnizca test araci degil; mimari kalite geri bildirim mekanizmasidir.
Partial mock ve sanal zaman
Bazi siniflarin yalnizca bir metodu mock'lanir; digerleri gerçek kalir. Sanal zaman kutuphaneleri (NSubstitute ile birlikte veya Java Clock) gecikme ve zamanlayici testlerini mock'suz cozebilir. Her partial mock kararinin gerekcesi test basinda yorum olarak belgelenmelidir.
Örnek karşılaştırma tablosu
- E-posta gonderimi onemsiz: NullObject dummy yeterli.
- Belirli fiyat dondurme: Stub exchange rate.
- Siparis kaydi okuma/yazma: Fake in-memory repo.
- Odeme çağrısı sayisi: Spy payment gateway.
- Tam odeme sozlesmesi: Mock with Verify.
Test smell: mock hell
Bir test dosyasinda yuz satir setup varsa mock hell'e girilmistir. Çözüm: builder pattern ile test verisi, facade ile bağımlılık azaltma, veya entegrasyon testine kaydirma. Code review'da setup satir sayisi / assert satir sayisi orani izlenir; 5:1 uzeri kok neden analizi tetikler.
Legacy kodda test double gecisi
Static DateTime.Now çağrısı test edilemiyorsa once seam eklenir: DateTimeProvider soyutlamasi. Legacy new ile olusturulan bağımlılıklar constructor enjeksiyonuna tasinir. Adim adim fake repository ile veritabanı bagimliligi azaltilir. Her refactor dalgasi davranisi koruyan karakterizasyon testleri ile korunur.
Test double dokumantasyonu
Ekip wiki'sinde hangi arayüz icin hangi double turunun tercih edildigi tablo halinde tutulur. Yeni geliştiriciler Moq yerine fake kullanma kararinin nedenini anlar. Örnek kod parcalari internal test kutuphanesinde paylasilir.
State-based ve interaction-based testing
Freeman ve Pryce interaction-based testi mock Verify agirlikli, state-based testi ise son durum assert agirlikli ayirir. Odeme sonrasi siparis durumu Paid ise state-based yeterli; PaymentGateway Charge tam olarak bir kez cagrildi mi sorusu interaction-based mock gerektirir. Karisik kullanım okunabilirligi dusurur; test basliginda hangi tur olduğu belirtilmeli.
Hata yolu testleri stub ile
Stub yalnizca mutlu yol icin degil hata senaryolari icin de uygundur. IInventory stub stok yok exception firlatir; OrderService uygun domain exception dondurur. Asiri mock kullanmadan timeout ve 503 simulasyonu sağlanır. Her hata yolu en az bir test ile korunmali.
El ile yazilan fake ve uretilen mock
Büyük arayuzlerde mock code generation hiz kazandirir ancak bakım yuku oluşturur. Dar port icin el ile fake struct daha sürdürülebilir kalir. InMemoryOrderRepository fake'i ekip icinde paylasilir; tekrar eden mock setup azalir. Generated mock her imza degisiminde yeniden üretim gerektirir.
Test verisi ile double kombinasyonu
Fake repository'ye Arrange adiminda builder ile entity eklenir; Act sonrasi fake uzerinden assert yapilir. Mock ve fake ayni testte karistirilmamali: depo fake, dis servis stub tercih edilir. Test okuyucusu hangi double'in ne rol oynadigini ilk bakista anlamali.
Doğru test double seçimi testlerin okunabilirligini, refactor direncini ve gerçek risk kapsamini dogrudan etkiler. Mock her problem icin cekic degildir; stub ve fake çoğu senaryoda daha sade ve sürdürülebilir çözüm sunar.