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.
🚀 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.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
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ę →