Yükleniyor...

C# Mock Framework Kullanımı (Moq, NSubstitute)

C#’ta Moq ve NSubstitute ile mock framework kullanımını öğrenin. Unit testlerde bağımlılıkları izole etme ve test senaryoları.

Gerçek hayattaki birim testlerde, test edilen sınıfın bağımlı olduğu diğer bileşenlerin davranışlarını izole etmek gerekir. İşte bu noktada mock framework’leri devreye girer. Moq ve NSubstitute gibi popüler kütüphaneler, sahte nesneler (mock objects) oluşturarak bağımlılıkların simüle edilmesini sağlar. Böylece yalnızca test edilmek istenen sınıfın davranışı kontrol edilir.


Mock Nedir?

Mock, bir sınıfın gerçek bağımlılıkları yerine kullanılan sahte nesnedir. Bu sayede test sırasında:


// Örnek senaryo: Email gönderim servisine bağımlı bir kullanıcı kaydı
public interface IEmailServisi
{
    void Gonder(string adres, string mesaj);
}

public class KullaniciKaydi
{
    private readonly IEmailServisi _emailServisi;

    public KullaniciKaydi(IEmailServisi emailServisi)
    {
        _emailServisi = emailServisi;
    }

    public void Kaydet(string ad)
    {
        // Kaydetme işlemleri...
        _emailServisi.Gonder("admin@site.com", $"{ad} başarıyla kaydedildi!");
    }
}

Bu sınıfı test ederken gerçek e-posta göndermek istemeyiz. Bu durumda mock email servisi kullanırız.


Moq Framework Nedir?

Moq, .NET ekosisteminde en popüler mock framework’lerinden biridir. Fluent API kullanımıyla kolay okunur ve güçlü doğrulama (verification) desteği sunar.


// NuGet üzerinden yükleme:
dotnet add package Moq

using Moq;
using Xunit;

public class KullaniciKaydiTests
{
    [Fact]
    public void Kaydet_MailGonderilmeli()
    {
        // Arrange
        var mockMail = new Mock<IEmailServisi>();
        var kayit = new KullaniciKaydi(mockMail.Object);

        // Act
        kayit.Kaydet("Ahmet");

        // Assert
        mockMail.Verify(
            m => m.Gonder("admin@site.com", "Ahmet başarıyla kaydedildi!"),
            Times.Once); // Metot 1 kez çağrılmalı
    }
}

Verify() metodu, belirli bir davranışın gerçekten gerçekleştiğini test eder. Böylece davranış tabanlı test (behavior verification) yapılabilir.


Mock Davranışlarını Belirleme

Moq ile yalnızca doğrulama değil, aynı zamanda sahte metotların dönüş değerlerini de tanımlayabilirsiniz.


public interface IStokServisi
{
    int Getir(string urunKodu);
}

public class UrunSatis
{
    private readonly IStokServisi _stok;
    public UrunSatis(IStokServisi stok) => _stok = stok;

    public bool SatisYap(string urunKodu)
    {
        int adet = _stok.Getir(urunKodu);
        return adet > 0;
    }
}

public class UrunSatisTests
{
    [Fact]
    public void Stokta_Varsa_SatisBasariliOlmali()
    {
        var mock = new Mock<IStokServisi>();
        mock.Setup(s => s.Getir("ABC123")).Returns(10);

        var satis = new UrunSatis(mock.Object);

        bool sonuc = satis.SatisYap("ABC123");

        Assert.True(sonuc);
    }
}

Setup() metodu, sahte nesnenin belirli çağrılarda nasıl davranacağını tanımlar. Returns() ile geri dönecek değer belirlenir.


Callback Kullanımı

Moq’ta Callback() metodu, bir metot çağrıldığında yan etki (side effect) eklemek için kullanılır. Örneğin, test sırasında log yazmak veya sayacı artırmak gibi.


int sayac = 0;
var mock = new Mock<IEmailServisi>();
mock.Setup(m => m.Gonder(It.IsAny<string>(), It.IsAny<string>()))
    .Callback(() => sayac++);

var kayit = new KullaniciKaydi(mock.Object);
kayit.Kaydet("Zeynep");

Assert.Equal(1, sayac);

Bu örnekte, Gonder() her çağrıldığında sayaç artırılır. Bu yöntem, testlerde olay takibi için çok kullanışlıdır.


It Helper Sınıfları

Moq, parametre kontrolleri için It sınıfını sağlar. Bu sayede yalnızca belirli parametreleri test edebilir veya wildcard kullanabilirsiniz.


mock.Verify(m =>
    m.Gonder(It.IsAny<string>(),
             It.Is<string>(msg => msg.Contains("Ahmet"))),
    Times.Once);

NSubstitute Framework Nedir?

NSubstitute, Moq’a benzer şekilde mock oluşturmayı sağlayan sade bir API sunar. Özellikle okunabilirliği yüksektir ve “Arrange–Act–Assert” düzenini doğal biçimde destekler.


// NuGet üzerinden yükleme:
dotnet add package NSubstitute

using NSubstitute;
using Xunit;

public class KullaniciKaydiTests
{
    [Fact]
    public void Kaydet_MailGonderilmeli()
    {
        var email = Substitute.For<IEmailServisi>();
        var kayit = new KullaniciKaydi(email);

        kayit.Kaydet("Mehmet");

        email.Received(1).Gonder("admin@site.com", "Mehmet başarıyla kaydedildi!");
    }
}

Received() metodu, metot çağrısının gerçekleşip gerçekleşmediğini kontrol eder. Syntax olarak daha doğal bir dil (okunabilir testler) sağlar.


Davranış Tanımlama (Returns ve When)

NSubstitute’ta Returns() ve When() metotlarıyla mock davranışları tanımlanabilir.


var stok = Substitute.For<IStokServisi>();
stok.Getir("P001").Returns(5);

var satis = new UrunSatis(stok);
bool sonuc = satis.SatisYap("P001");

Assert.True(sonuc);

// Çağrı sırasında yan etki ekleme:
stok.When(s => s.Getir("P001"))
    .Do(_ => Console.WriteLine("Stok servisi çağrıldı."));

When(...).Do(...) yapısı, belirli bir çağrı gerçekleştiğinde özel eylemler tanımlamak için idealdir.


Moq vs NSubstitute Karşılaştırması

ÖzellikMoqNSubstitute
Kullanım StiliFluent API (Setup/Verify)Doğal dil (Returns/Received)
OkunabilirlikYüksekDaha yüksek
PerformansBiraz daha hızlıOrtalama
Callback DesteğiVar (.Callback())Var (.Do())
PopülerlikÇok yaygın (xUnit ile sık kullanılır)Daha az ama modern syntax

Gerçek Hayat Örneği: Sipariş Servisi Testi

Aşağıdaki örnek, hem Moq hem de NSubstitute ile servis bağımlılığının test edildiği bir senaryoyu gösterir.


public interface ISiparisServisi
{
    void Kaydet(string urun);
}

public class EPostaServisi
{
    public virtual void Gonder(string mesaj)
    {
        Console.WriteLine($"Mail gönderildi: {mesaj}");
    }
}

public class SiparisIslemi
{
    private readonly ISiparisServisi _siparis;
    private readonly EPostaServisi _posta;

    public SiparisIslemi(ISiparisServisi siparis, EPostaServisi posta)
    {
        _siparis = siparis;
        _posta = posta;
    }

    public void IslemiTamamla(string urun)
    {
        _siparis.Kaydet(urun);
        _posta.Gonder($"{urun} siparişi tamamlandı!");
    }
}

// Moq ile test
var siparisMock = new Mock<ISiparisServisi>();
var postaMock = new Mock<EPostaServisi>();

var islem = new SiparisIslemi(siparisMock.Object, postaMock.Object);
islem.IslemiTamamla("Laptop");

siparisMock.Verify(s => s.Kaydet("Laptop"), Times.Once);
postaMock.Verify(p => p.Gonder("Laptop siparişi tamamlandı!"), Times.Once);

// NSubstitute ile test
var siparis = Substitute.For<ISiparisServisi>();
var posta = Substitute.For<EPostaServisi>();

var islem2 = new SiparisIslemi(siparis, posta);
islem2.IslemiTamamla("Laptop");

siparis.Received(1).Kaydet("Laptop");
posta.Received(1).Gonder("Laptop siparişi tamamlandı!");

Performans ve En İyi Uygulamalar


TL;DR

  • Mock Framework’leri, testlerde bağımlılıkları izole etmek için sahte nesneler oluşturur.
  • Moq: Fluent API yapısında, popüler ve güçlü doğrulama desteği sunar.
  • NSubstitute: Daha okunabilir syntax ve doğal akış sağlar.
  • Setup() ve Returns() → Davranış tanımı yapar.
  • Verify() / Received() → Çağrı doğrulaması yapar.
  • Mock’lar gerçek sistemlerle etkileşmeden yalnızca hedef sınıfı test etmeye odaklanır.

İlişkili Makaleler

C# Delegates ve Events

C#’ta delegate ve event kavramlarını öğrenin. Olay tabanlı programlama, callback mantığı ve kullanım senaryoları örneklerle anlatılıyor.