|
| 1 | +--- |
| 2 | +title: aweXpect |
| 3 | +sidebar_position: -1 |
| 4 | +--- |
| 5 | + |
| 6 | +import Tabs from '@theme/Tabs'; |
| 7 | +import TabItem from '@theme/TabItem'; |
| 8 | + |
| 9 | +[](https://www.nuget.org/packages/aweXpect) |
| 10 | +[](https://www.nuget.org/packages/aweXpect.Core) |
| 11 | + |
| 12 | +`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. |
| 13 | + |
| 14 | +```csharp |
| 15 | +await Expect.That(result).IsEqualTo(42); |
| 16 | +``` |
| 17 | + |
| 18 | +> ``` |
| 19 | +> Expected result to |
| 20 | +> be equal to 42, |
| 21 | +> but it was 41 |
| 22 | +> ``` |
| 23 | +
|
| 24 | +## Why aweXpect? |
| 25 | +
|
| 26 | +<Tabs groupId="aweXpectFeatures"> |
| 27 | +<TabItem value="async" label="Async-first" default> |
| 28 | +
|
| 29 | +Every expectation is awaited. The same fluent chain works for sync values, `Task`, `IAsyncEnumerable`, exceptions and HTTP responses — there is no parallel `…Async()` API to remember. |
| 30 | +
|
| 31 | +```csharp |
| 32 | +// Plain value |
| 33 | +await Expect.That(result).IsEqualTo(42); |
| 34 | +
|
| 35 | +// Task<T> |
| 36 | +await Expect.That(_users.GetAsync(id)) |
| 37 | + .Satisfies(u => u.IsActive); |
| 38 | +
|
| 39 | +// IAsyncEnumerable<T> — the cancellation token flows through the stream |
| 40 | +await Expect.That(_orders.StreamAsync()) |
| 41 | + .Contains(o => o.IsPriority) |
| 42 | + .WithCancellation(cancellationToken); |
| 43 | +
|
| 44 | +// Delegate that throws |
| 45 | +await Expect.That(() => _payments.ChargeAsync(-1m)) |
| 46 | + .ThrowsExactly<ArgumentOutOfRangeException>(); |
| 47 | +``` |
| 48 | +
|
| 49 | +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(...)` — no thread-static assertion scope. |
| 50 | + |
| 51 | +</TabItem> |
| 52 | +<TabItem value="performant" label="Performant"> |
| 53 | + |
| 54 | +The happy path — passing assertions, the common case in a healthy test suite — is the path that has been optimised. See the [benchmarks](https://awexpect.com/benchmarks) for numbers. |
| 55 | + |
| 56 | +</TabItem> |
| 57 | +<TabItem value="extensible" label="Extensible"> |
| 58 | + |
| 59 | +The [`aweXpect.Core`](https://www.nuget.org/packages/aweXpect.Core) package is intentionally kept stable so extensions don't fight over versions. Add expectations for any type with extension methods on `IThat<TType>`: |
| 60 | + |
| 61 | +```csharp |
| 62 | +public static AndOrResult<string, IThat<string>> IsAbsolutePath( |
| 63 | + this IThat<string> subject) |
| 64 | + => new(subject.Get().ExpectationBuilder.AddConstraint((it, grammars) |
| 65 | + => new IsAbsolutePathConstraint(it, grammars)), |
| 66 | + subject); |
| 67 | + |
| 68 | +// usage |
| 69 | +await Expect.That("/var/log").IsAbsolutePath(); |
| 70 | +``` |
| 71 | + |
| 72 | +The full walkthrough is at [Write your own extension](/write-your-own-extension). |
| 73 | + |
| 74 | +</TabItem> |
| 75 | +<TabItem value="frameworks" label="Test-framework agnostic"> |
| 76 | + |
| 77 | +xUnit (v2 and v3), NUnit (v3 and v4), MSTest and TUnit are detected automatically. The matching framework-native exception is thrown, so failures integrate cleanly with each runner. |
| 78 | + |
| 79 | +```csharp |
| 80 | +// xUnit |
| 81 | +[Fact] public async Task IsActive() |
| 82 | + => await Expect.That(user.IsActive).IsTrue(); |
| 83 | + |
| 84 | +// NUnit |
| 85 | +[Test] public async Task IsActive() |
| 86 | + => await Expect.That(user.IsActive).IsTrue(); |
| 87 | + |
| 88 | +// MSTest |
| 89 | +[TestMethod] public async Task IsActive() |
| 90 | + => await Expect.That(user.IsActive).IsTrue(); |
| 91 | + |
| 92 | +// TUnit |
| 93 | +[Test] public async Task IsActive() |
| 94 | + => await Expect.That(user.IsActive).IsTrue(); |
| 95 | +``` |
| 96 | + |
| 97 | +</TabItem> |
| 98 | +</Tabs> |
| 99 | + |
| 100 | +## Migrating from another assertion library? |
| 101 | + |
| 102 | +The [aweXpect.Migration](https://github.com/aweXpect/aweXpect.Migration) analyzer ships code-fix providers that rewrite most FluentAssertions and `xunit.Assert` call sites for you. See the [migration guide](./getting-started#migration) for the full walkthrough. |
| 103 | + |
| 104 | +## Companion libraries |
| 105 | + |
| 106 | +`aweXpect` ships a small core with focused extensions for common ecosystems: |
| 107 | + |
| 108 | +- **[aweXpect.Json](https://github.com/aweXpect/aweXpect.Json)** — expectations for `System.Text.Json`. |
| 109 | +- **[aweXpect.Web](https://github.com/aweXpect/aweXpect.Web)** — expectations for `HttpClient` and `HttpResponseMessage`. |
| 110 | +- **[aweXpect.Reflection](https://github.com/aweXpect/aweXpect.Reflection)** — expectations for reflection types. |
| 111 | +- **[aweXpect.Testably](https://github.com/aweXpect/aweXpect.Testably)** — expectations for [Testably.Abstractions](https://github.com/Testably/Testably.Abstractions) file and time systems. |
| 112 | +- **[aweXpect.Mockolate](https://github.com/aweXpect/aweXpect.Mockolate)** — expectations for [Mockolate](https://github.com/aweXpect/Mockolate) mock interactions. |
| 113 | + |
| 114 | +## Where to go next |
| 115 | + |
| 116 | +- **[Getting Started](./getting-started)** — install the package and write your first expectation. |
| 117 | +- **[Migration](./getting-started#migration)** — automated code fixes for moving from FluentAssertions or `xunit.Assert`. |
| 118 | +- **[Advanced](./advanced)** — multiple expectations, cancellation, customization, and more. |
| 119 | +- **[Write your own extension](../extensions/write-extensions)** — add expectations for your own types using `aweXpect.Core`. |
0 commit comments