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

xUnit vs NUnit vs MSTest — który framework testowy wybrać w C#?

Wszystkie trzy frameworki robią to samo: uruchamiają metody testowe i sprawdzają, czy wynik jest zgodny z oczekiwaniem. Różnią się atrybutami, sposobem parametryzacji i tym, jak bardzo są “opiniotwórcze”. Poniżej ten sam test w każdym z nich — różnice widać od razu.

xUnit — domyślny wybór w nowych projektach .NET

using Xunit;

public class KalkulatorTests
{
    [Fact]
    public void Dodaj_DwieLiczby_ZwracaSume()
    {
        var wynik = Kalkulator.Dodaj(2, 3);
        Assert.Equal(5, wynik);
    }

    [Theory]
    [InlineData(2, 3, 5)]
    [InlineData(-1, 1, 0)]
    [InlineData(0, 0, 0)]
    public void Dodaj_RozneLiczby_ZwracaPoprawnaSume(int a, int b, int oczekiwany)
    {
        Assert.Equal(oczekiwany, Kalkulator.Dodaj(a, b));
    }
}

xUnit nie ma atrybutu [SetUp] — zamiast tego setup wspólny dla testów w klasie umieszcza się w konstruktorze, a teardown w IDisposable.Dispose():

public class BazaDanychTests : IDisposable
{
    private readonly TestDbContext _kontekst;

    public BazaDanychTests() // odpowiednik [SetUp] — wykonuje się przed KAŻDYM testem
    {
        _kontekst = TworzKontekstTestowy();
    }

    public void Dispose() // odpowiednik [TearDown]
    {
        _kontekst.Dispose();
    }
}

To świadoma decyzja projektowa xUnit: każdy test dostaje świeżą instancję klasy testowej, więc nie ma współdzielonego stanu między testami przez pola klasy — trudniej przypadkiem napisać test zależny od kolejności wykonania innych testów.

NUnit — starszy, bardziej rozbudowany

using NUnit.Framework;

[TestFixture]
public class KalkulatorTests
{
    [SetUp]
    public void PrzedKazdymTestem()
    {
        // wykonuje się przed każdym testem w klasie
    }

    [Test]
    public void Dodaj_DwieLiczby_ZwracaSume()
    {
        var wynik = Kalkulator.Dodaj(2, 3);
        Assert.That(wynik, Is.EqualTo(5));
    }

    [TestCase(2, 3, 5)]
    [TestCase(-1, 1, 0)]
    [TestCase(0, 0, 0)]
    public void Dodaj_RozneLiczby_ZwracaPoprawnaSume(int a, int b, int oczekiwany)
    {
        Assert.That(Kalkulator.Dodaj(a, b), Is.EqualTo(oczekiwany));
    }

    [TearDown]
    public void PoKazdymTescie()
    {
        // sprzątanie po każdym teście
    }
}

NUnit ma jawne [SetUp]/[TearDown] oraz dodatkowo [OneTimeSetUp]/[OneTimeTearDown] (wykonują się raz na całą klasę, nie przed/po każdym teście) — więcej kontroli, ale też więcej atrybutów do zapamiętania. Składnia asercji Assert.That(wartość, Is.EqualTo(...)) jest bardziej czytelna w złożonych warunkach (Is.GreaterThan, Contains.Item, Throws.TypeOf<T>()).

MSTest — natywny wybór Microsoftu

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class KalkulatorTests
{
    [TestInitialize]
    public void PrzedKazdymTestem()
    {
        // odpowiednik [SetUp]
    }

    [TestMethod]
    public void Dodaj_DwieLiczby_ZwracaSume()
    {
        var wynik = Kalkulator.Dodaj(2, 3);
        Assert.AreEqual(5, wynik);
    }

    [DataTestMethod]
    [DataRow(2, 3, 5)]
    [DataRow(-1, 1, 0)]
    [DataRow(0, 0, 0)]
    public void Dodaj_RozneLiczby_ZwracaPoprawnaSume(int a, int b, int oczekiwany)
    {
        Assert.AreEqual(oczekiwany, Kalkulator.Dodaj(a, b));
    }
}

MSTest jest utrzymywany bezpośrednio przez Microsoft i domyślnie zintegrowany z Visual Studio (żadnych dodatkowych pakietów do “po prostu kliknij Run Tests”). Składnia jest zbliżona do NUnit ([TestClass]/[TestMethod] zamiast [TestFixture]/[Test]), ale historycznie miał mniej możliwości niż NUnit — różnica ta w ostatnich wersjach się zaciera.

Kluczowe różnice w jednej tabeli

xUnitNUnitMSTest
Test jednostkowy[Fact][Test][TestMethod]
Test parametryzowany[Theory] + [InlineData][TestCase][DataTestMethod] + [DataRow]
Setup przed testemkonstruktor klasy[SetUp][TestInitialize]
Teardown po teścieIDisposable.Dispose()[TearDown][TestCleanup]
Setup raz na klasęIClassFixture<T>[OneTimeSetUp][ClassInitialize]
Domyślna asercjaAssert.Equal(oczekiwany, wynik)Assert.That(wynik, Is.EqualTo(oczekiwany))Assert.AreEqual(oczekiwany, wynik)
Stan współdzielony między testaminie (nowa instancja per test)tak (chyba że jawnie wyczyścisz)tak (chyba że jawnie wyczyścisz)

Który wybrać?

xUnit — domyślny wybór dla nowych projektów .NET Core/.NET 5+. Używany w dokumentacji Microsoft, w szablonach dotnet new, i w większości nowych projektów open-source. Konwencja “nowa instancja na test” wymusza pisanie testów niezależnych od siebie, co długoterminowo daje mniej ukrytych bugów w samych testach.

NUnit — dobry wybór, jeśli pracujesz w starszym projekcie, który już go używa, albo potrzebujesz bogatszej biblioteki asercji (Assert.That z fluent-style warunkami) bez dodawania osobnego pakietu jak FluentAssertions.

MSTest — sensowny, jeśli firma/zespół ma zasadę “tylko oficjalne narzędzia Microsoft”, albo integrujesz testy z istniejącym pipeline’em opartym już na MSTest. W nowym, niezależnym projekcie rzadko jest pierwszym wyborem.

W praktyce różnice między frameworkami są mniejsze niż mogłyby się wydawać — logika testowa (Arrange-Act-Assert) i to, co testujesz, ma większe znaczenie niż wybrany framework. Zobacz TDD w praktyce w C# i Unit Testy w C# z xUnit i FluentAssertions, gdzie xUnit jest używany w realnym projekcie.

Podsumowanie

xUnit, NUnit i MSTest różnią się głównie nazwami atrybutów i podejściem do współdzielenia stanu między testami — xUnit tworzy nową instancję klasy testowej na każdy test (mniej przypadkowych zależności), NUnit i MSTest współdzielą stan przez pola klasy z jawnym [SetUp]/[TearDown]. Dla nowego projektu .NET domyślnym wyborem jest xUnit; NUnit i MSTest mają sens głównie w kontekście istniejącego kodu, który już ich używa.

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

1 comment

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