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ę:
- Arrange – przygotuj scenę (obiekty, dane wejściowe)
- Act – wykonaj akcję (uruchom testowaną metodę)
- 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!

