Skip to content
dotnet-testing-awesome-assertions-guide logo

dotnet-testing-awesome-assertions-guide

Using AwesomeAssertions for fluent and readable test assertion skill. Used when writing clear assertions, comparing objects, validating collections, handling complex comparisons. Covers Should(), BeEquivalentTo(), Contain(), ThrowAsync() and complete API. Keywords: assertions, awesome assertions...

rudironsoni/Synaxis0installs2stars

SKILL.md

Full skill instructions

Source: kevintsengtw/​dotnet-testing-agent-skills (MIT). Ported into dotnet-agent-harness.

AwesomeAssertions Fluent Assertion Guide

This skill provides a complete guide for writing high-quality test assertions using AwesomeAssertions, covering basic syntax, advanced techniques, and best practices.

Applicable Scenarios

Use this skill when asked to perform the following tasks:

  • Write clear, highly readable test assertions
  • Compare complex objects or collection contents
  • Verify exception throwing and messages
  • Use fluent syntax (Should/​Be/​Contain) for test validation
  • Replace native Assert with AwesomeAssertions

About AwesomeAssertions

AwesomeAssertions is a community fork version of FluentAssertions, using Apache 2.0 license, completely free with no commercial usage restrictions.

Core Features

  • Fully Free: Apache 2.0 license, suitable for commercial projects
  • Fluent Syntax: Supports natural language style method chaining
  • Rich Assertions: Covers objects, collections, strings, numbers, exceptions, and various other types
  • Excellent Error Messages: Provides detailed and easy-to-understand failure information
  • High Performance: Optimized implementation ensures test execution efficiency
  • Extensible: Supports custom Assertion methods

Relationship with FluentAssertions

AwesomeAssertions is a community fork of FluentAssertions, main differences:

ItemFluentAssertionsAwesomeAssertions
LicenseCommercial projects require paymentApache 2.0 (completely free)
NamespaceFluentAssertionsAwesomeAssertions
API CompatibilityOriginalHighly compatible
Community SupportOfficial maintenanceCommunity maintenance

Installation and Setup

NuGet Package Installation

# .NET CLI
dotnet add package AwesomeAssertions

# Package Manager Console
Install-Package AwesomeAssertions
```text

### csproj Setup (Recommended)

```xml
<ItemGroup>
  <PackageReference Include="AwesomeAssertions" Version="9.1.0" PrivateAssets="all" />
</​ItemGroup>
```text

### Namespace Import

```csharp
using AwesomeAssertions;
using Xunit;
```text

---

## 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, patterns, case-insensitive comparison |
| **Number** | `BeGreaterThan()`, `BeInRange()`, `BeApproximately()` | Comparison, range, floating point precision |
| **Collection** | `HaveCount()`, `Contain()`, `BeEquivalentTo()`, `AllSatisfy()` | Count, content, order, conditions |
| **Exception** | `Throw<T>()`, `NotThrow()`, `WithMessage()`, `WithInnerException()` | Exception types, messages, nested exceptions |
| **Async** | `ThrowAsync<T>()`, `CompleteWithinAsync()` | Async exceptions and completion validation |

> Full syntax examples and code please refer to [references/​core-assertions-syntax.md](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 Extension

Create domain-specific extension methods, like `product.Should().BeValidProduct()`, and reusable exclusion extensions like `ExcludingAuditFields()`.

Refer to [templates/​custom-assertions-template.cs](templates/custom-assertions-template.cs) for complete implementation.

> Full examples please refer to [references/​complex-object-assertions.md](references/complex-object-assertions.md)

---

## Performance Optimization Strategies

- **Large data**: First use `HaveCount()` for quick count check, then sample validation (avoid full `BeEquivalentTo`)
- **Selective comparison**: Use anonymous objects + `ExcludingMissingMembers()` to only validate key properties

```csharp
// Selective property comparison — only validate key fields
order.Should().BeEquivalentTo(new
{
    CustomerId = 123,
    TotalAmount = 999.99m,
    Status = "Pending"
}, options => options.ExcludingMissingMembers());
```text

---

## Best Practices and Team Standards

### Test Naming Conventions

Follow `Method_Scenario_ExpectedResult` pattern (e.g., `CreateUser_WithValidEmail_ShouldReturnEnabledUser`).

### Error Message Optimization

Add `because` string in assertions to provide clear failure context:

```csharp
result.IsSuccess.Should().BeFalse("because negative payment amounts are not allowed");
```text

### AssertionScope Usage

Use `AssertionScope` to collect multiple failure messages, display all problems at once:

```csharp
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");
}
```text

---

## Common Scenarios and Solutions

| Scenario | Key Technique |
|------|----------|
| API response validation | `BeEquivalentTo()` + `Including()` selective comparison |
| Database entity validation | `BeEquivalentTo()` + `Excluding()` exclude auto-generated fields |
| Event validation | Subscribe to capture events then validate properties one by one |

> Full code examples please refer to [references/​common-scenarios.md](references/common-scenarios.md)

---

## Troubleshooting

### Problem 1: BeEquivalentTo fails but objects look the same

**Reason**: May contain auto-generated fields or timestamps

**Solution**:

```csharp
// Exclude dynamic fields
actual.Should().BeEquivalentTo(expected, options => options
    .Excluding(x => x.Id)
    .Excluding(x => x.CreatedAt)
    .Excluding(x => x.UpdatedAt)
);
```text

### Problem 2: Collection order different causes failure

**Reason**: Collection order is different

**Solution**:

```csharp
// Use BeEquivalentTo ignore order
actual.Should().BeEquivalentTo(expected); // Does not check order

// Or explicitly specify need to check order
actual.Should().Equal(expected); // Checks order
```text

### Problem 3: Floating point comparison fails

**Reason**: Floating point precision issues

**Solution**:

```csharp
// Use precision tolerance
actualValue.Should().BeApproximately(expectedValue, 0.001);
```text

---

## When to Use This Skill

### Applicable Scenarios

Write unit tests or integration tests
Need to validate complex object structures
Compare API responses or database entities
Need clear failure messages
Establish domain-specific testing standards

### Not Applicable Scenarios

Performance testing (use dedicated benchmarking tools)
Load testing (use K6, JMeter, etc.)
UI testing (use Playwright, Selenium)

---

## Integration with Other Skills

### Integration with unit-test-fundamentals

First use `unit-test-fundamentals` to establish test structure, then use this skill to write assertions:

```csharp
[Fact]
public void Calculator_Add_TwoPositiveNumbers_ShouldReturnSum()
{
    // Arrange - follow 3A Pattern
    var calculator = new Calculator();

    // Act
    var result = calculator.Add(2, 3);

    // Assert - use AwesomeAssertions
    result.Should().Be(5);
}
```text

### Integration with test-naming-conventions

Use `test-naming-conventions` naming conventions, combined with this skill's assertions:

```csharp
[Fact]
public void CreateUser_WithValidData_ShouldReturnEnabledUser()
{
    var user = userService.CreateUser("[email protected]");

    user.Should().NotBeNull()
        .And.BeOfType<User>();
    user.IsActive.Should().BeTrue();
}
```text

### Integration with xunit-project-setup

Install and use AwesomeAssertions in projects created with `xunit-project-setup`.

---

## Reference Resources

### Original Articles

This skill content is extracted from "Old School Software Engineer's Testing Practice - 30 Day Challenge" series:

- **Day 04 - AwesomeAssertions Basic Application and Practical Techniques**
  - Ironman Article: https://ithelp.ithome.com.tw/articles/10374188
  - Sample Code: https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day04

- **Day 05 - AwesomeAssertions Advanced Techniques and Complex Scenario Applications**
  - Ironman Article: https://ithelp.ithome.com.tw/articles/10374425
  - Sample Code: https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day05

### Official Resources

- **AwesomeAssertions GitHub**: https://github.com/AwesomeAssertions/AwesomeAssertions
- **AwesomeAssertions Official Documentation**: https://awesomeassertions.org/

### Related Articles

- **Fluent Assertions License Change Discussion**: https://www.dotblogs.com.tw/mrkt/2025/04/19/152408

---

## Summary

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

1. **Fluent Syntax**: Makes test code more readable
2. **Rich Assertions**: Covers various data types
3. **Custom Extensions**: Establish domain-specific assertions
4. **Performance Optimization**: Handle large data scenarios
5. **Completely Free**: Apache 2.0 license with no commercial restrictions

Remember: Good assertions not only validate results but clearly express expected behavior and provide useful diagnostic information when failing.

Refer to [templates/​assertion-examples.cs](templates/assertion-examples.cs) for more practical examples.