Migrate the test suite from xUnit + FluentAssertions to NUnit

All 44 tests, same names, same coverage — verified by diffing the runner's test
list before and after.

The load-bearing change is fixture lifecycle. xUnit builds a new test-class
instance per test, so a `readonly TestDatabase _db = new()` field gave every
test its own database. NUnit reuses one instance for the whole class, so those
field initialisers and constructors would have shared a single database across a
class and let tests read each other's rows. Setup moved into [SetUp]/[TearDown]
via a new ServiceTestFixture base class, which also collapses the per-class
service wiring that was duplicated five times.

Assertions are now Assert.That with the constraint model:

  Should().Be(x)              -> Is.EqualTo(x)
  Should().BeNull()           -> Is.Null
  Should().HaveCount(n)       -> Has.Count.EqualTo(n)
  Should().Equal(a, b)        -> Is.EqualTo(new[] { a, b })
  Should().BeEquivalentTo(..) -> Is.EquivalentTo(..)
  Should().Contain("x")       -> Does.Contain("x")
  Should().OnlyHaveUniqueItems() -> Is.Unique
  ThrowAsync<T>().WithMessage("*m*")
      -> Throws.TypeOf<T>().With.Message.Contains("m")

FluentAssertions' `.Which` chains became plain indexed asserts, grouped in
Assert.Multiple so a failure reports every broken expectation in the case rather
than stopping at the first.

Because a framework migration can quietly produce vacuously-passing tests,
spot-checked four conversions by mutation — breaking the code under an async
Assert.Multiple block, a sync one, a Throws constraint, and a collection
ordering assert. All four failed as they should, confirming the assertions are
live and that NUnit bound the async lambdas to AsyncTestDelegate rather than
silently accepting them as async void.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
This commit is contained in:
James Wampler
2026-08-06 12:11:20 -07:00
co-authored by Claude Opus 5
parent 8394843255
commit 30e0c6926e
9 changed files with 510 additions and 434 deletions
@@ -9,6 +9,11 @@ namespace NovelSoftware.Tests;
/// in-memory provider means the tests exercise the same relational behaviour the app
/// ships with — cascade deletes, foreign keys and all.
/// </summary>
/// <remarks>
/// NUnit reuses one fixture instance across every test in a class, so this must be built
/// in <c>[SetUp]</c> and disposed in <c>[TearDown]</c>. A field initialiser would share
/// one database for the whole class and let tests see each other's rows.
/// </remarks>
public class TestDatabase : IDisposable
{
private readonly SqliteConnection _connection;
@@ -32,5 +37,6 @@ public class TestDatabase : IDisposable
{
Context.Dispose();
_connection.Dispose();
GC.SuppressFinalize(this);
}
}