Testing with xUnit
ProfessNet tests .NET code with xUnit — the default framework of the .NET ecosystem. For mocking we use Moq, and we write readable assertions with FluentAssertions. This lesson shows our patterns.
Anatomy of an xUnit test
[Fact] is a single test case. We stick to the AAA layout: 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);
}
}
ProfessNet standard: the test name follows the
Method_Condition_ExpectedResultscheme. One test = one behavior.
Theories — many cases
[Theory] with [InlineData] is a single test run for many data sets.
[Theory]
[InlineData("CORP", "corp")]
[InlineData(" Dmz ", "dmz")]
[InlineData("", "")]
public void Normalize_TrimsAndLowercases(string input, string expected)
{
Assert.Equal(expected, ForestName.Normalize(input));
}
FluentAssertions — readable assertions
result.Should().BeTrue();
probes.Should().HaveCount(2).And.Contain(p => p.Host == "dc01");
act.Should().ThrowAsync<ProbeTimeoutException>();
ProfessNet standard: we write assertions with FluentAssertions — the error message is readable and the intent is clear. We avoid long chains of raw
Assert.Equalfor complex objects.
Mocking with Moq
We replace external dependencies with a mock to test a unit in isolation.
[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);
}
Shared setup
We initialize shared state in the test class constructor (xUnit creates a new instance for each test — isolation is guaranteed).
public class ProbeServiceTests
{
private readonly Mock<IProbeRepository> _repo = new();
private readonly ProbeService _sut;
public ProbeServiceTests()
{
_sut = new ProbeService(_repo.Object, NullLogger<ProbeService>.Instance);
}
}
| Attribute / tool | For what |
|---|---|
[Fact] | a single case |
[Theory] + [InlineData] | many data sets, one test |
Mock<T> (Moq) | replacing a dependency |
.Should() (FluentAssertions) | readable assertions |
IClassFixture<T> | expensive setup shared across the class |
Tip:
_sut(system under test) is the name for the unit being tested — it makes reading easier. OnlyVerifya mock when the interaction is part of the contract, not an implementation detail.
xUnit + Moq + FluentAssertions is our standard toolkit. Fast unit tests on
Application/Domain logic give confidence with every refactor and are an
entry condition for a PR into the main branch.