Testy z xUnit
ProfessNet testuje kod .NET w xUnit — domyślnym frameworku ekosystemu .NET. Do mockowania używamy Moq, a asercje piszemy czytelnie przez FluentAssertions. Ta lekcja pokazuje nasze wzorce.
Anatomia testu xUnit
[Fact] to pojedynczy przypadek testowy. Trzymamy się układu AAA: Arrange,
Act, Assert.
public class ProbeTests
{
[Fact]
public void IsActive_WhenLastSeenRecent_ReturnsTrue()
{
// Arrange
var probe = new Probe { LastSeen = DateTime.UtcNow.AddMinutes(-1) };
// Act
var result = probe.IsActive();
// Assert
Assert.True(result);
}
}
Standard ProfessNet: nazwa testu w schemacie
Metoda_Warunek_OczekiwanyWynik. Jeden test = jedno zachowanie.
Teorie — wiele przypadków
[Theory] z [InlineData] to jeden test uruchamiany dla wielu danych.
[Theory]
[InlineData("CORP", "corp")]
[InlineData(" Dmz ", "dmz")]
[InlineData("", "")]
public void Normalize_TrimsAndLowercases(string input, string expected)
{
Assert.Equal(expected, ForestName.Normalize(input));
}
FluentAssertions — czytelne asercje
result.Should().BeTrue();
probes.Should().HaveCount(2).And.Contain(p => p.Host == "dc01");
act.Should().ThrowAsync<ProbeTimeoutException>();
Standard ProfessNet: asercje piszemy w FluentAssertions — komunikat błędu jest czytelny, a intencja jasna. Unikamy ciągów surowych
Assert.Equalprzy złożonych obiektach.
Mockowanie z Moq
Zewnętrzne zależności podmieniamy mockiem, żeby testować jednostkę w izolacji.
[Fact]
public async Task RunScanAsync_EnqueuesJob()
{
// Arrange
var repo = new Mock<IProbeRepository>();
repo.Setup(r => r.GetHostsAsync("corp", It.IsAny<CancellationToken>()))
.ReturnsAsync(new[] { "dc01" });
var service = new ProbeService(repo.Object, NullLogger<ProbeService>.Instance);
// Act
var result = await service.RunScanAsync("corp");
// Assert
result.HostCount.Should().Be(1);
repo.Verify(
r => r.GetHostsAsync("corp", It.IsAny<CancellationToken>()),
Times.Once);
}
Współdzielony setup
Wspólny stan inicjalizujemy w konstruktorze klasy testowej (xUnit tworzy nową instancję na każdy test — izolacja jest gwarantowana).
public class ProbeServiceTests
{
private readonly Mock<IProbeRepository> _repo = new();
private readonly ProbeService _sut;
public ProbeServiceTests()
{
_sut = new ProbeService(_repo.Object, NullLogger<ProbeService>.Instance);
}
}
| Atrybut / narzędzie | Do czego |
|---|---|
[Fact] | pojedynczy przypadek |
[Theory] + [InlineData] | wiele danych, jeden test |
Mock<T> (Moq) | podmiana zależności |
.Should() (FluentAssertions) | czytelne asercje |
IClassFixture<T> | drogi setup współdzielony w klasie |
Wskazówka:
_sut(system under test) to nazwa testowanej jednostki — ułatwia czytanie. Mock weryfikujVerifytylko wtedy, gdy interakcja jest częścią kontraktu, a nie szczegółem implementacji.
xUnit + Moq + FluentAssertions to nasz standardowy zestaw. Szybkie testy
jednostkowe na logice Application/Domain dają pewność przy każdym refaktorze
i są warunkiem wejścia PR-a do głównej gałęzi.