Unit Testy w C#

Unit Testy w C# z xUnit i FluentAssertions – Testowanie BattleService w Clean Architecture

Unit Testy w C# z xUnit i FluentAssertions – Testowanie BattleService w Clean Architecture

Piszesz kod w C#, ale boisz się, że po refaktoryzacji coś się rozsypie?
Czas to zmienić!
W tym wpisie nauczysz się, jak pisać testy jednostkowe (unit testy) w C# z wykorzystaniem xUnit i FluentAssertions.
Na przykładzie BattleService z projektu Clean Architecture RPG zobaczysz, że testowanie nie jest teorią – to Twoja najlepsza broń przeciwko bugom.

🧱 Dlaczego testy jednostkowe są kluczowe w Clean Architecture?

Clean Architecture została zaprojektowana z myślą o testowalności.
Cała idea interfejsów, separacji warstw i braku zależności zewnętrznych ma sens dopiero wtedy, gdy możesz testować każdą część systemu osobno.

Bez testów – nawet najpiękniejsza architektura staje się labiryntem.
Z testami – masz pewność, że wszystko działa po każdej zmianie.

💥 Najważniejsze korzyści:

  • Pewność działania kodu po refaktoryzacji
  • Łatwiejsze debugowanie i utrzymanie projektu
  • Szybszy rozwój bez obaw o regresję
  • Kod, który dokumentuje się sam

Zobacz też: Klasy i Obiekty w C# na przykładzie gry RPG


⚙️ Konfiguracja środowiska testowego

Zacznij od stworzenia projektu testowego w folderze Tests:

dotnet new xunit -n DevHobby.Code.RPG.Core.Tests

# Utwórz projekt i katalog jednocześnie
dotnet new xunit -n DevHobby.Code.RPG.Core.Tests -o DevHobby.Code.RPG.Core.Tests

Dodaj niezbędne pakiety do pliku .csproj:

<ItemGroup>
  <PackageReference Include="xunit" Version="2.4.2" />
  <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
  <PackageReference Include="FluentAssertions" Version="6.12.0" />
  <PackageReference Include="Moq" Version="4.20.69" />
  <PackageReference Include="coverlet.collector" Version="6.0.0" />
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
</ItemGroup>

🧰 Co to daje?

  • xUnit – framework do pisania testów
  • FluentAssertions – czytelne asercje (result.Should().Be(…))
  • Moq – do mockowania zależności (przyda się później)
  • Coverlet – mierzy pokrycie kodu testami

🧠 Pierwsze testy jednostkowe: BattleService

Warstwa Core w Clean Architecture to serce aplikacji – czysta logika biznesowa bez zależności zewnętrznych.
Idealne miejsce, by rozpocząć naukę testowania!


🧩 Klasa BattleService

public class BattleService : IBattleService
{
    public Postac Symuluj(IList<Postac> uczestnicy)
    {
        // Walka trwa dopóki nie zostanie 1 żywy uczestnik
        // Zwraca zwycięzcę
    }
}

To właśnie tę klasę będziemy testować.


🧪 Testy jednostkowe w praktyce

Utwórz plik BattleServiceTests.cs:

using DevHobby.Code.RPG.Core.Entities;
using DevHobby.Code.RPG.Core.Services;
using FluentAssertions;

namespace DevHobby.Code.RPG.Tests.Core;

public class BattleServiceTests
{
    private readonly BattleService _battleService = new();

    [Fact]
    public void Symuluj_WithTwoCharacters_ShouldReturnWinner()
    {
        // ARRANGE
        var wojownik = new Wojownik("Testowy Wojownik");
        var goblin = new Goblin();
        var participants = new List<Postac> { wojownik, goblin };

        // ACT
        var winner = _battleService.Symuluj(participants);

        // ASSERT
        winner.Should().NotBeNull();
        winner.PunktyZycia.Should().BeGreaterThan(0);
        participants.Count(p => p.PunktyZycia > 0).Should().Be(1);
    }
}

🧩 Zasada AAA (Arrange–Act–Assert)

Każdy test powinien mieć tę samą strukturę:

  1. Arrange – przygotuj scenę (obiekty, dane wejściowe)
  2. Act – wykonaj akcję (uruchom testowaną metodę)
  3. Assert – sprawdź wynik (czy zachowanie jest poprawne)

To prosty, ale niezwykle skuteczny wzorzec.


⚔️ Testowanie przypadków skrajnych (edge cases)

Edge cases to momenty, w których kod może się „wyłożyć”: pusta lista, brak danych, martwe obiekty itp.
Nie ignoruj ich — to tam czają się bugi!

[Fact]
public void Symuluj_WithEmptyList_ShouldThrowException()
{
    var participants = new List<Postac>();
    var action = () => _battleService.Symuluj(participants);
    action.Should().Throw<InvalidOperationException>();
}

[Fact]
public void Symuluj_WithDeadCharacters_ShouldHandleCorrectly()
{
    var martwy = new Zombi("Martwy Wojownik");
    var goblin = new Goblin();
    var participants = new List<Postac> { martwy, goblin };

    var winner = _battleService.Symuluj(participants);

    winner.Should().Be(goblin);
}

private class Zombi : Bohater
{
    public Zombi(string imie) : base(imie, 0, 30) { }
}

✅ Testy przechodzą – logika działa!
Twój kod jest gotowy na refaktoryzację bez strachu.


📈 Co zyskujesz, pisząc testy jednostkowe

  • Większe zaufanie do kodu – każda zmiana od razu daje feedback
  • Automatyczna dokumentacja – testy pokazują, jak działa logika
  • Mniej bugów, mniej stresu, więcej czasu na rozwój projektu

🚀 Podsumowanie

W tym wpisie:
✅ Skonfigurowałeś projekt testowy w .NET
✅ Napisałeś testy jednostkowe dla BattleService
✅ Poznałeś AAA Pattern i FluentAssertions
✅ Zrozumiałeś znaczenie testów w Clean Architecture

Kolejny krok?
👉 Testowanie Application Layer z mockowaniem zależności za pomocą Moq.


Jeśli ten artykuł pomógł Ci  💡 Zostaw komentarz pod postem – napisz, jak testujesz swoje projekty!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *