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:
co-authored by
Claude Opus 5
parent
8394843255
commit
30e0c6926e
@@ -0,0 +1,45 @@
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Shared plumbing for the service tests: a fresh in-memory database and a matching set
|
||||
/// of services per test.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The setup lives in <c>[SetUp]</c> rather than a constructor or field initialisers
|
||||
/// because NUnit builds one fixture instance for the whole class — anything created once
|
||||
/// would leak state from one test into the next.
|
||||
/// </remarks>
|
||||
public abstract class ServiceTestFixture
|
||||
{
|
||||
protected TestDatabase Db { get; private set; } = null!;
|
||||
protected TagService Tags { get; private set; } = null!;
|
||||
protected ProjectService Projects { get; private set; } = null!;
|
||||
protected CharacterService Characters { get; private set; } = null!;
|
||||
protected ChapterService Chapters { get; private set; } = null!;
|
||||
protected SceneService Scenes { get; private set; } = null!;
|
||||
protected BeatService Beats { get; private set; } = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUpFixture()
|
||||
{
|
||||
Db = new TestDatabase();
|
||||
Tags = new TagService(Db.Context);
|
||||
Projects = new ProjectService(Db.Context);
|
||||
Characters = new CharacterService(Db.Context, Tags);
|
||||
Chapters = new ChapterService(Db.Context, Tags);
|
||||
Scenes = new SceneService(Db.Context);
|
||||
Beats = new BeatService(Db.Context, Tags);
|
||||
|
||||
OnSetUp();
|
||||
}
|
||||
|
||||
/// <summary>Runs after the services exist, for per-class seed data.</summary>
|
||||
protected virtual void OnSetUp()
|
||||
{
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDownFixture() => Db.Dispose();
|
||||
}
|
||||
Reference in New Issue
Block a user