This skill provides a complete guide to high-quality test assertions using AwesomeAssertions, covering basic syntax, advanced techniques, and best practices.
About AwesomeAssertions
AwesomeAssertions is a community fork of FluentAssertions, licensed under Apache 2.0 , completely free with no commercial usage restrictions.
Core Features
- Completely Free: Apache 2.0 license, suitable for commercial projects
- Fluent Syntax: Supports natural language style with method chaining
- Rich Assertions: Covers various types such as objects, collections, strings, numbers, exceptions, and more
- Excellent Error Messages: Provides detailed and easy-to-understand failure information
- High Performance: Optimized implementation ensures test execution efficiency
- Extensible: Supports custom Assertions methods
Relationship with FluentAssertions
AwesomeAssertions is a community fork of FluentAssertions, with the main differences:
| Item | FluentAssertions | AwesomeAssertions |
|---|---|---|
| License | Commercial projects require payment | Apache 2.0 (completely free) |
| Namespace | FluentAssertions |
AwesomeAssertions |
| API Compatibility | Original | Highly compatible |
| Community Support | Official maintenance | Community maintenance |
Installation and Setup
NuGet Package Installation
# .NET CLI
dotnet add package AwesomeAssertions
# Package Manager Console
Install-Package AwesomeAssertions
csproj Configuration (Recommended)
<ItemGroup>
<PackageReference Include="AwesomeAssertions" Version="9.4.0" PrivateAssets="all" />
</ItemGroup>
Namespace Import
using AwesomeAssertions;
using Xunit;
Core Assertions Syntax
All Assertions start with .Should() , combined with fluent method chaining.
| Category | Common Methods | Description |
|---|---|---|
| Object | NotBeNull(), BeOfType<T>(), BeEquivalentTo() |
Null, type, equality checks |
| String | Contain(), StartWith(), MatchRegex(), BeEquivalentTo() |
Content, pattern, case-insensitive comparison |
| Numeric | BeGreaterThan(), BeInRange(), BeApproximately() |
Comparison, range, floating-point precision |
| Numeric Collection | EqualApproximately(), NotEqualApproximately() |
Numeric collection approximate comparison (9.4+) |
| Collection | HaveCount(), Contain(), BeEquivalentTo(), AllSatisfy() |
Count, content, order, conditions |
| Exception | Throw<T>(), NotThrow(), WithMessage(), WithInnerException() |
Exception type, message, inner exception |
| Asynchronous | ThrowAsync<T>(), CompleteWithinAsync() |
Asynchronous exceptions and completion verification |
For complete syntax examples and code, see references/core-assertions-syntax.md
Advanced Techniques: Complex Object Comparison
Use BeEquivalentTo() with options for deep object comparison:
- Exclude Properties:
options.Excluding(u => u.Id)— Exclude auto-generated fields - Dynamic Exclusion:
options.Excluding(ctx => ctx.Path.EndsWith("At"))— Exclude by pattern - Circular References:
options.IgnoringCyclicReferences().WithMaxRecursionDepth(10)
Advanced Techniques: Custom Assertions Extensions
Create domain-specific extension methods, such as product.Should().BeValidProduct(), and reusable exclusion extensions such as ExcludingAuditFields()。
Refer to templates/custom-assertions-template.cs for the complete implementation.
For a complete example, see references/complex-object-assertions.md
Performance Optimization Strategies
- Large Data: First use
HaveCount()to quickly check the count, then sample and verify (avoid fullBeEquivalentTo) - Selective Comparison: Use anonymous objects +
ExcludingMissingMembers()to verify only key properties
// 選擇性屬性比對 — 只驗證關鍵欄位
order.Should().BeEquivalentTo(new
{
CustomerId = 123,
TotalAmount = 999.99m,
Status = "Pending"
}, options => options.ExcludingMissingMembers());
New Features in Versions 9.2-9.4
Approximate Comparison of Numeric Collections — [Not]EqualApproximately(9.4+)
For INumber<T> numeric collections, an approximate equality assertion has been added (requires .NET 8+):
// 驗證數值集合在容差範圍內近似相等
var actual = new[] { 1.001f, 2.002f, 3.003f };
actual.Should().EqualApproximately(new[] { 1f, 2f, 3f }, 0.01f);
// 驗證集合不在容差範圍內近似相等
actual.Should().NotEqualApproximately(new[] { 10f, 20f, 30f }, 0.01f);
vs.
BeApproximately()differences:BeApproximately()is used for a single numeric value,EqualApproximately()is used for numeric collections and compares each element one by one. When the tolerance is 0,EqualApproximatelybehaves the same asEqual。
Setting the Maximum Number of Items for Collection Formatting (9.4+)
When an assertion fails, collections display the first 32 items by default. You can now customize this via FormattingOptions :
// 在 AssertionScope 中設定集合顯示上限
using var scope = new AssertionScope();
scope.FormattingOptions.MaxItems = 100; // 預設為 32
largeCollection.Should().BeEquivalentTo(expected);
It can also be overridden via a custom Formatter:
class LargeCollectionFormatter : EnumerableValueFormatter
{
protected override int MaxItems => 100;
public override bool CanHandle(object value) => value is IEnumerable<MyEntity>;
}
Collection Occurrence Constraints (9.2+)
Contain The method adds occurrence count constraints:
var numbers = new[] { 1, 2, 2, 3, 3, 3 };
numbers.Should().Contain(2, AtLeast.Once());
numbers.Should().Contain(3, Exactly.Times(3));
Null Safety Improvements — [return: NotNull](9.4+)
All Should() and assertion methods have been annotated with [return: NotNull] , allowing the compiler's nullable analysis to track them correctly:
// 9.4 前:Should() 後仍視為可能 null,需加 ! 運算子
var user = GetUser();
user.Should().NotBeNull();
user!.Name.Should().Be("test"); // 需要 ! 消除警告
// 9.4 後:Should().NotBeNull() 返回帶 [NotNull] 標註,編譯器自動推斷非 null
user.Should().NotBeNull();
user.Name.Should().Be("test"); // 不再需要 !
Excluding Member Names (9.3+)
BeEquivalentTo Supports excluding members by name:
actual.Should().BeEquivalentTo(expected, options => options
.Excluding(ctx => ctx.Name == "InternalState"));
Best Practices and Team Standards
Test Naming Conventions
Follow the 方法_情境_預期結果 pattern (e.g., CreateUser_有效電子郵件_應回傳啟用的使用者)。
Error Message Optimization
Add the because string in assertions to provide clear failure context:
result.IsSuccess.Should().BeFalse("because negative payment amounts are not allowed");
Using AssertionScope
Use AssertionScope to collect multiple failure messages and display all problems at once:
using (new AssertionScope())
{
user.Should().NotBeNull("User creation should not fail");
user.Id.Should().BeGreaterThan(0, "User should have valid ID");
user.Email.Should().NotBeNullOrEmpty("Email is required");
}
Common Scenarios and Solutions
| Scenario | Key Techniques |
|---|---|
| API Response Validation | BeEquivalentTo() + Including() Selective comparison |
| Database Entity Validation | BeEquivalentTo() + Excluding() Exclude auto-generated fields |
| Event Validation | Subscribe to capture events, then verify properties one by one |
For complete code examples, see references/common-scenarios.md
Troubleshooting
Problem 1: BeEquivalentTo fails but the objects look the same
Cause: May contain auto-generated fields or timestamps
Solution:
// 排除動態欄位
actual.Should().BeEquivalentTo(expected, options => options
.Excluding(x => x.Id)
.Excluding(x => x.CreatedAt)
.Excluding(x => x.UpdatedAt)
);
Problem 2: Failure due to different collection order
Cause: Collection order is different
Solution:
// 使用 BeEquivalentTo 忽略順序
actual.Should().BeEquivalentTo(expected); // 不檢查順序
// 或明確指定需要檢查順序
actual.Should().Equal(expected); // 檢查順序
Problem 3: Floating-point comparison fails
Cause: Floating-point precision issue
Solution:
// 單一數值使用精度容差
actualValue.Should().BeApproximately(expectedValue, 0.001);
// 數值集合使用 EqualApproximately(9.4+,需 .NET 8+)
actualValues.Should().EqualApproximately(expectedValues, 0.001);
When to Use This Skill
Applicable Scenarios
When writing unit tests or integration tests When complex object structures need to be verified When comparing API responses or database entities When clear failure messages are needed When establishing domain-specific testing standards
Inapplicable Scenarios
Performance testing (use dedicated benchmarking tools) Load testing (use K6, JMeter, etc.) UI testing (use Playwright, Selenium)
Coordination with Other Skills
Pairing with unit-test-fundamentals
First use unit-test-fundamentals to establish the test structure, then use this skill to write assertions:
[Fact]
public void Calculator_Add_兩個正數_應回傳總和()
{
// Arrange - 遵循 3A Pattern
var calculator = new Calculator();
// Act
var result = calculator.Add(2, 3);
// Assert - 使用 AwesomeAssertions
result.Should().Be(5);
}
Pairing with test-naming-conventions
Use the naming convention of test-naming-conventions , together with this skill's assertions:
[Fact]
public void CreateUser_有效資料_應回傳啟用使用者()
{
var user = userService.CreateUser("test@example.com");
user.Should().NotBeNull()
.And.BeOfType<User>();
user.IsActive.Should().BeTrue();
}
Pair with xunit-project-setup
Install and use AwesomeAssertions in the project created by xunit-project-setup .
Output Format
- Generate test assertions using AwesomeAssertions fluent syntax
- Use method chains such as Should().Be/BeEquivalentTo/Contain
- Include examples of object comparison, collection verification, and exception assertions
- Provide the .csproj package reference (AwesomeAssertions)
Reference Resources
Original Articles
The content of this skill is distilled from the "Old-School Software Engineer's Testing Practice - 30-Day Challenge" article series:
-
Day 04 - AwesomeAssertions Basic Applications and Practical Techniques
-
Day 05 - AwesomeAssertions Advanced Techniques and Complex Scenario Applications
Official Resources
- AwesomeAssertions GitHub:https://github.com/AwesomeAssertions/AwesomeAssertions
- AwesomeAssertions Official Documentation:https://awesomeassertions.org/
Related Articles
- Discussion of Fluent Assertions License Changes:https://www.dotblogs.com.tw/mrkt/2025/04/19/152408
Summary
AwesomeAssertions provides a powerful and readable assertion syntax and is an important tool for writing high-quality tests. Through:
- Fluent syntax: makes test code more readable
- Rich assertions: covers various data types (9.4 adds approximate comparison for numeric collections)
- Custom extensions: create domain-specific assertions
- Performance optimization: handles large-data scenarios (9.4 adds a setting for the collection display limit)
- Completely free: Apache 2.0 license with no commercial restrictions
- Null safety: since 9.4,
[return: NotNull]improves nullable analysis
Remember: good assertions not only verify results, but also clearly express expected behavior and provide useful diagnostic information when they fail.
Refer to templates/assertion-examples.cs for more practical examples.