aweXpect
aweXpect is a fluent assertion library for .NET. It turns test verifications into readable sentences and produces
failure messages that explain in plain English what was expected and what actually happened.
await That(message).IsEqualTo("It's a compliment");
Expected that message
is equal to "It's a compliment",
but it was "It's a complement", which differs at index 12:
↓ (actual)
"…a complement"
"…a compliment"
↑ (expected)
This documentation describes aweXpect v4, which is available as a pre-release; Getting started shows how to install it. If you are upgrading from v3, read From aweXpect 3.x.
Why aweXpect?
- Async-first
- Extensible
- Performant
- Any runner
- Native AOT
Every expectation is awaited. The same fluent chain works for sync values, Task, IAsyncEnumerable, exceptions and
HTTP responses (with the separate aweXpect.Web package): there is no
parallel …Async() API to remember.
// Plain value
await That(result).IsEqualTo(42);
// Task<T>
await That(_artists.GetAsync(id))
.Satisfies(a => a.IsActive);
// IAsyncEnumerable<T> - the cancellation token flows through the stream
await That(_radio.StreamAsync())
.Contains(s => s.IsLive)
.WithCancellation(cancellationToken);
// Delegate that throws
await That(() => _player.SetVolumeAsync(-1))
.ThrowsExactly<ArgumentOutOfRangeException>();
Because the chain is awaited, Because(...) and WithCancellation(...) attach once at the end instead of being
threaded through every method. Multiple expectations compose directly via Expect.ThatAll(...), without a
thread-static assertion scope.
The aweXpect.Core package is intentionally kept stable so extensions
don't fight over versions. Ready-made extension packages cover JSON
(aweXpect.Json), HTTP (aweXpect.Web),
architecture tests (aweXpect.Reflection), file and time systems
(aweXpect.Testably) and mocks
(aweXpect.Mockolate).
Add expectations for any other type as extension methods on IThat<T>. For a simple condition, a source generator
writes the expectation, its negation and the failure messages for you:
public static partial class ThatTrack
{
[GenerateExpectation("Is{Not}RadioFriendly", "is {not} radio friendly")]
private static bool IsRadioFriendly(Track track)
=> track.Duration < TimeSpan.FromMinutes(4);
}
// usage
await That(track).IsRadioFriendly();
The full walkthrough is at Extending aweXpect.
The happy path (passing assertions, the common case in a healthy test suite) is the path that has been optimized.
Comparing strings, ignoring case
See the benchmarks for the full set.
[Fact] // xUnit
[Test] // NUnit / TUnit
[TestMethod] // MSTest
public async Task IsActive()
=> await That(artist.IsActive).IsTrue();
xUnit, NUnit, MSTest and TUnit are detected automatically, see the
supported versions: a failure usually throws the assertion exception of the test
framework, see failed expectations for the exact types. Fail
and Skip fail or skip a test without an expectation.
aweXpect and aweXpect.Core are trimmable and AOT compatible on .NET 8 and later, see
Native AOT and trimming.
Where to go next
- Getting started: install the package and write your first test.
- Find the expectation you need: the page that lists the expectations for your subject, from values and collections to delegates, events and the extension packages.
- Migration: the aweXpect.Migration analyzer ships code fixes that rewrite most FluentAssertions and
xunit.Assertcall sites for you; the guide also covers upgrading from aweXpect 2 or 3. - Concepts: awaiting, negation, combining,
null, options, time and cancellation, and the configuration of the defaults. - Setup and integration: test frameworks, target frameworks and Native AOT.
- Extending aweXpect: add expectations for your own types using
aweXpect.Core.