Overview
Standardise test assertions on open-source libraries, preferring AwesomeAssertions (the community fork of FluentAssertions) for fluent, readable assertions with better error messages. Enforce OSS license revalidation for all test dependencies.
When to Use
- Establishing or reviewing test conventions for a new project
- Introducing or upgrading assertion libraries in existing projects
- Migrating from FluentAssertions to AwesomeAssertions
- Reviewing PRs that add new test dependencies
- Evaluating assertion library license compliance
Core Workflow
- Verify the assertion library is open-source (check license)
- Add AwesomeAssertions package reference to test projects
- Update using statements to reference AwesomeAssertions namespace
- Write fluent assertions using Should() extension methods
- Use BeEquivalentTo for DTOs with exclusions for generated fields
- Use BeCloseTo for time assertions to avoid brittle exact matches
- Run tests to verify assertions work correctly
Core
When to use
- Establishing or reviewing test conventions.
- Introducing or upgrading assertion libraries.
Defaults
- Prefer AwesomeAssertions (formerly FluentAssertions) for fluent assertions (open source).
- Test framework selection is separate (e.g., xUnit) and may be governed elsewhere.
Note: AwesomeAssertions is the community-maintained fork of FluentAssertions after the original project returned to open source governance. The API is largely compatible, making migration straightforward for existing FluentAssertions users.
Review rules
- Do not introduce assertion libraries that are not open source.
- Apply the OSS license revalidation gate (see
dotnet-open-source-first-governance).
Load: examples
- Assert equivalence for DTOs and read models.
- Assert exception types and messages where stable.
- Use precise assertions for time, GUIDs, collections, and nullable scenarios.
Load: advanced
- Avoid brittle assertions that over-specify implementation details.
- Prefer structural assertions for API contract tests.
- For non-deterministic values (timestamps/IDs), assert invariants and ranges.
Load: enforcement
- Reject PRs that introduce non-open-source assertion libraries.
- Require a documented license verification for new/updated test dependencies.
Load: migration
Migration Checklist (FluentAssertions → AwesomeAssertions)
- Update NuGet packages: Replace
FluentAssertionswithAwesomeAssertions - Update using statements:
using FluentAssertions;→using AwesomeAssertions; - Run tests: Verify all existing tests pass (API is largely compatible)
- Check breaking changes: Review release notes for any API differences
- Update CI: Ensure package restore works in CI pipeline
- Document decision: Add ADR noting the migration and rationale
Sample Assertion Diff
// Package reference
- <PackageReference Include="FluentAssertions" Version="6.12.0" />
+ <PackageReference Include="AwesomeAssertions" Version="7.0.0" />
// Using statement
- using FluentAssertions;
+ using AwesomeAssertions;
// Assertions remain the same
result.Should().NotBeNull();
result.Items.Should().HaveCount(3);
result.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
Common Assertion Patterns
// Object equivalence (ignoring specific properties)
actual.Should().BeEquivalentTo(expected, options => options
.Excluding(x => x.Id)
.Excluding(x => x.CreatedAt));
// Collection assertions
items.Should().ContainSingle(x => x.Name == "Test");
items.Should().BeInAscendingOrder(x => x.Priority);
items.Should().AllSatisfy(x => x.IsValid.Should().BeTrue());
// Exception assertions
action.Should().Throw<ValidationException>()
.WithMessage("*required*");
// Async assertions
await action.Should().ThrowAsync<NotFoundException>();
xUnit vs AwesomeAssertions Comparison
| xUnit Assert | AwesomeAssertions | Benefit |
|---|---|---|
Assert.Equal(expected, actual) |
actual.Should().Be(expected) |
Fluent, readable |
Assert.NotNull(obj) |
obj.Should().NotBeNull() |
Chaining support |
Assert.Contains(item, list) |
list.Should().Contain(item) |
Better error messages |
Assert.Throws<T>(action) |
action.Should().Throw<T>() |
Message assertions |
Red Flags - STOP
These statements indicate assertion anti-patterns:
| Thought | Reality |
|---|---|
| "xUnit Assert is good enough" | Fluent assertions provide better error messages and chaining |
| "Any assertion library will do" | Ensure OSS license compliance; verify before adopting |
| "Assert exact timestamps" | Use BeCloseTo for time; exact matches are brittle |
| "Assert full object equality" | Use BeEquivalentTo with exclusions; ignore generated fields |
| "Over-specify implementation details" | Assert behaviour and outcomes, not implementation |
| "Skip license verification" | All test dependencies need OSS license revalidation |
Converted and distributed by TomeVault — claim your Tome and manage your conversions.