Skip to content

chore(test): replace FluentAssertions with xunit assertions - #295

Merged
nevridge merged 2 commits into
mainfrom
feature/replace-fluentassertions-with-xunit
Oct 21, 2026
Merged

nevridge merged 2 commits into
mainfrom
feature/replace-fluentassertions-with-xunit

Conversation

@nevridge

Copy link
Copy Markdown
Owner

Summary

Removes the FluentAssertions dependency (Xceed Community License, not allow-listed in dependency-review.yml) and rewrites the test assertions to xunit's built-in Assert.*. Docs and agent instructions that described FluentAssertions are updated too.

Closes #294

What changed

  • TaskFlow.Api.Tests.csproj: removed the FluentAssertions 8.10.0 package reference.
  • All 36 test files under TaskFlow.Api.Tests/: removed using FluentAssertions; and rewrote every .Should() call (863 lines) to Assert.*. No tests were added, removed, skipped or restructured.
  • Docs and agent files: README.md, TaskFlow.Api.Tests/README.md, docs/CONTRIBUTING.md, docs/SERVICE_REGISTRATION_PATTERN.md, .claude/agents/backend-builder.md, .claude/agents/test-verifier.md now describe xunit Assert.*.
  • No production code, generated client or frontend changes. .github/dependabot.yml already had no FluentAssertions ignore entry.

Semantic-preservation decisions

  • BeEquivalentTo on collections: Assert.Equal(expected.Order(), actual.Order()) or Assert.Single, so extra or missing items still fail. Validation-failure lists use Assert.Equivalent(..., strict: true).
  • TaskRepositoryTests entity BeEquivalentTo: Assert.Same(result, savedTask), since FindAsync returns the tracked instance (at least as strict, avoids cyclic graph walking).
  • ContainInOrder: filter to the expected items, then Assert.Equal (subsequence in order).
  • WithMessage("*x*"): Assert.Throws<T> then Assert.Contains(..., StringComparison.OrdinalIgnoreCase) to match the old case-insensitive wildcard.
  • NotThrow / NotThrowAsync: Assert.Null(Record.Exception(...)) / Record.ExceptionAsync.
  • BeCloseTo: Assert.Equal(DateTime.UtcNow, actual, TimeSpan.FromSeconds(5)), with Assert.NotNull first for nullable values.
  • RouteValues: TryGetValue plus Assert.Equal(<int>, value) (boxed ints confirmed by passing tests).
  • "Because" messages kept: xunit message overload where one exists, otherwise a // comment directly above the assertion.

Test results

  • Baseline (unchanged code): 417 passed, 0 skipped; 91.6% line coverage with the ci.yml flags and excludes (42.57% with plain --coverlet, which has no excludes and does not meet the gate on its own).
  • After: 417 passed, 0 skipped; 93.08% line coverage with the ci.yml flags, gate threshold 75.
  • [Fact]/[Theory] attributes (383) and data rows (54) are identical to main.
  • dotnet format TaskFlow.sln --verify-no-changes: clean.
  • rg for FluentAssertions / .Should() across code and the six docs/agent files: no matches.
  • Mutation checks (extra collection item, wrong order, wrong message substring, thrown exception in a not-throw test) each made the matching test fail; mutations reverted.
  • Independent test-verifier and implementation-validator runs: 0 Critical, 0 Important.

Residual findings (cosmetic, no behavior change)

  • A few redundant ! after Assert.NotNull remain.
  • About 15 redundant parentheses around awaited expressions, e.g. Assert.Equal(0, (await ...)).
  • A few .Count() calls on IEnumerable<T> locals.

Follow-up

@nevridge
nevridge force-pushed the feature/replace-fluentassertions-with-xunit branch from 7a21ab1 to dacab87 Compare October 21, 2026 12:42
@nevridge
nevridge merged commit 412026c into main Oct 21, 2026
15 checks passed
@nevridge
nevridge deleted the feature/replace-fluentassertions-with-xunit branch October 21, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(test): replace FluentAssertions with xunit assertions (license)

1 participant