🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Moq w C# — podstawy mockowania zależności

Klasa, którą chcesz przetestować, często zależy od innych klas — repozytorium bazy danych, klienta HTTP, wysyłki e-maili. Moq pozwala podstawić w ich miejsce “atrapę”, która zachowuje się dokładnie tak, jak jej każesz, bez uruchamiania prawdziwej bazy danych czy serwera SMTP.

Problem, który Moq rozwiązuje

public class RabatService
{
    private readonly IKlientRepository _repozytorium;
    public RabatService(IKlientRepository repozytorium) => _repozytorium = repozytorium;

    public decimal ObliczRabat(int klientId)
    {
        var klient = _repozytorium.ZnajdzPoId(klientId);
        return klient.LiczbaZamowien > 10 ? 0.15m : 0.05m;
    }
}

Żeby przetestować ObliczRabat, potrzebujesz IKlientRepository — ale nie chcesz w teście jednostkowym łączyć się z prawdziwą bazą danych. Moq tworzy fałszywą implementację interfejsu w locie.

Mock<T> i Setup — sterowanie zwracaną wartością

using Moq;
using Xunit;

[Fact]
public void ObliczRabat_KlientZDuzaLiczbaZamowien_Zwraca15Procent()
{
    var mockRepo = new Mock<IKlientRepository>();
    mockRepo.Setup(r => r.ZnajdzPoId(5))
            .Returns(new Klient { Id = 5, LiczbaZamowien = 20 });

    var serwis = new RabatService(mockRepo.Object);
    var rabat = serwis.ObliczRabat(5);

    Assert.Equal(0.15m, rabat);
}

mockRepo.Object to fałszywy IKlientRepository, który zamiast pytać bazę danych, po prostu zwraca to, co ustawiłeś w Setup. RabatService nie wie (i nie musi wiedzieć), że dostał atrapę zamiast prawdziwego repozytorium — testujesz wyłącznie logikę ObliczRabat.

It.IsAny<T>() — dowolna wartość argumentu

mockRepo.Setup(r => r.ZnajdzPoId(It.IsAny<int>()))
        .Returns(new Klient { Id = 999, LiczbaZamowien = 3 });

It.IsAny<int>() mówi: “dla dowolnego int przekazanego do ZnajdzPoId, zwróć tego samego klienta”. Przydatne, gdy dokładna wartość argumentu nie ma znaczenia dla testowanego scenariusza.

Verify — sprawdzanie, czy metoda została wywołana

[Fact]
public void ZlozZamowienie_PoprawneDane_WysylaPotwierdzenieEmail()
{
    var mockPowiadomienia = new Mock<IPowiadomienia>();
    var serwis = new ZamowienieService(mockPowiadomienia.Object);

    serwis.ZlozZamowienie(new Zamowienie { KlientEmail = "test@example.com" });

    mockPowiadomienia.Verify(
        p => p.WyslijAsync("test@example.com", It.IsAny<string>()),
        Times.Once);
}

Verify nie sprawdza wartości zwróconej — sprawdza, czy dana metoda w ogóle została wywołana, ile razy (Times.Once, Times.Never, Times.Exactly(3)) i z jakimi argumentami. To kluczowe dla metod, które nie zwracają wartości, tylko wywołują efekt uboczny (wysyłka e-maila, zapis do logu).

Throws — symulowanie błędu zależności

mockRepo.Setup(r => r.ZnajdzPoId(It.IsAny<int>()))
        .Throws(new InvalidOperationException("Baza danych niedostępna"));

Assert.Throws<InvalidOperationException>(() => serwis.ObliczRabat(5));

Symulowanie awarii zależności (baza danych padła, zewnętrzne API zwróciło błąd) bez faktycznego wyłączania czegokolwiek — sprawdzasz, czy Twój kod poprawnie reaguje na wyjątek.

MockBehavior.Strict — gdy każde wywołanie musi być jawnie skonfigurowane

var mockRepo = new Mock<IKlientRepository>(MockBehavior.Strict);
mockRepo.Setup(r => r.ZnajdzPoId(5)).Returns(new Klient { Id = 5 });
// wywołanie ZnajdzPoId(6) na tym mocku rzuci wyjątkiem — nieskonfigurowane

Domyślnie (MockBehavior.Loose) niekonfigurowane wywołania zwracają wartość domyślną (null, 0, false) zamiast rzucać błąd. MockBehavior.Strict wymusza jawną konfigurację każdego wywołania — pomaga wyłapać scenariusz, w którym kod wywołuje zależność w nieoczekiwany sposób.

Kiedy NIE mockować

Mockuj granice systemu — repozytoria, klientów HTTP, wysyłkę e-maili, zegar systemowy. Nie mockuj prostych obiektów wartości (Adres, Pieniądze) ani własnej logiki domenowej — jeśli mockujesz zbyt dużo, test przestaje sprawdzać cokolwiek realnego i tylko powtarza to, co sam skonfigurowałeś w Setup.

Zobacz praktyczne zastosowanie Moq w kontekście pełnego projektu w Unit Testing Application Layer w .NET — Mockowanie zależności z Moq, gdzie mockowanie jest częścią większej serii o Clean Architecture.

Podsumowanie

Moq pozwala testować klasę w izolacji od jej zależności: Mock<T> tworzy atrapę interfejsu, Setup/Returns kontroluje co zwraca, It.IsAny<T>() dopasowuje dowolny argument, Verify sprawdza czy metoda została wywołana, a Throws symuluje awarię. Klucz to mockować granice systemu (I/O, sieć, czas), nie własną logikę biznesową — inaczej test przestaje cokolwiek weryfikować.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →