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

FluentAssertions w C# — czytelniejsze asercje w testach

Assert.Equal(oczekiwany, wynik) działa, ale gdy test padnie, komunikat błędu bywa lakoniczny. FluentAssertions to biblioteka, która zamienia asercje w czytelne zdania (wynik.Should().Be(oczekiwany)) i — ważniejsze — generuje dużo bardziej szczegółowe komunikaty, gdy test faili.

Podstawowa składnia — Should()

using FluentAssertions;
using Xunit;

[Fact]
public void Dodaj_DwieLiczby_ZwracaSume()
{
    var wynik = Kalkulator.Dodaj(2, 3);
    wynik.Should().Be(5);
}

Różnica widoczna dopiero, gdy test faili. Assert.Equal(5, wynik) przy błędzie da: Assert.Equal() Failure: Expected: 5, Actual: 4. FluentAssertions dla wynik.Should().Be(5) da bardziej opisowy komunikat wskazujący dokładnie, czego dotyczy porównanie — różnica rośnie drastycznie przy złożonych obiektach.

Asercje na stringach

string imie = "Mariusz Jurczenko";

imie.Should().StartWith("Mariusz");
imie.Should().Contain("Jurczenko");
imie.Should().NotBeNullOrEmpty();
imie.Should().HaveLength(17);

Asercje na kolekcjach

var liczby = new List<int> { 1, 2, 3, 4, 5 };

liczby.Should().HaveCount(5);
liczby.Should().Contain(3);
liczby.Should().BeInAscendingOrder();
liczby.Should().OnlyContain(x => x > 0);
liczby.Should().Equal(1, 2, 3, 4, 5); // dokładna kolejność i zawartość

Should().Equal(...) porównuje kolekcję element po elemencie — jeśli któryś się różni albo kolejność jest inna, komunikat błędu wskazuje dokładnie, na której pozycji nastąpiła rozbieżność.

BeEquivalentTo — porównanie całych obiektów bez przeciążania Equals

public record Zamowienie(int Id, string Klient, decimal Suma);

var oczekiwany = new Zamowienie(1, "Jan Kowalski", 299.99m);
var wynik = serwis.PobierzZamowienie(1);

wynik.Should().BeEquivalentTo(oczekiwany);

BeEquivalentTo porównuje obiekty właściwość-po-właściwości (przez refleksję), niezależnie od tego, czy typ nadpisał Equals. Jeśli wynik.Suma wynosi 199.99m zamiast 299.99m, komunikat błędu wskaże dokładnie tę jedną właściwość — nie trzeba pisać własnego porównania pole-po-polu ręcznie.

Asercje na wyjątkach

Action akcja = () => serwis.ObliczRabat(-1);

akcja.Should().Throw<ArgumentException>()
     .WithMessage("*nie może być ujemny*");

WithMessage obsługuje wildcardy (*) — nie musisz dopasowywać całego tekstu komunikatu, wystarczy fragment.

Łączenie warunków przez And

wynik.Should().NotBeNull()
     .And.BeOfType<Zamowienie>()
     .And.Match<Zamowienie>(z => z.Suma > 0);

.And pozwala połączyć kilka asercji na tym samym obiekcie w jeden czytelny łańcuch, zamiast pisać osobne linijki Assert.

Liczby zmiennoprzecinkowe — BeApproximately

double wynik = 0.1 + 0.2;
wynik.Should().BeApproximately(0.3, precision: 0.0001);

Bezpośrednie porównanie double przez == (albo Assert.Equal) jest zawodne z powodu błędów zaokrągleń — BeApproximately pozwala określić dopuszczalną tolerancję wprost w asercji.

Dlaczego to się opłaca w większych projektach

Im bardziej złożony obiekt porównujesz (zagnieżdżone listy, rekordy z wieloma polami), tym większa różnica w czytelności komunikatu błędu między Assert.Equal a Should().BeEquivalentTo(). W praktycznym projekcie zobacz Unit Testy w C# z xUnit i FluentAssertions, gdzie FluentAssertions jest używany do testowania BattleService w kontekście Clean Architecture.

Podsumowanie

FluentAssertions nie zmienia co testujesz — zmienia, jak czytelny jest zapis asercji i jak szczegółowy jest komunikat błędu, gdy test faili. Should().Be() dla prostych wartości, BeEquivalentTo() dla całych obiektów, Throw<T>() dla wyjątków i .And do łączenia warunków w jednym czytelnym łańcuchu — w większych projektach ta czytelność bezpośrednio przekłada się na szybszy czas diagnozy, gdy coś się psuje.

👨‍💻
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.

Dodaj komentarz

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ę →