SuperAPI AI Directory

dotnet-testing-awesome-assertions-guide

Testing & quality

View repository

Updated 10-15 23:45

About this skill

AwesomeAssertions Fluent Assertions Guide

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 full BeEquivalentTo)
  • 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,EqualApproximately behaves the same as Equal。

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:

Official Resources

Related Articles


Summary

AwesomeAssertions provides a powerful and readable assertion syntax and is an important tool for writing high-quality tests. Through:

  1. Fluent syntax: makes test code more readable
  2. Rich assertions: covers various data types (9.4 adds approximate comparison for numeric collections)
  3. Custom extensions: create domain-specific assertions
  4. Performance optimization: handles large-data scenarios (9.4 adds a setting for the collection display limit)
  5. Completely free: Apache 2.0 license with no commercial restrictions
  6. 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.