Assertivo 0.5.0

dotnet add package Assertivo --version 0.5.0
                    
NuGet\Install-Package Assertivo -Version 0.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Assertivo" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Assertivo" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="Assertivo" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Assertivo --version 0.5.0
                    
#r "nuget: Assertivo, 0.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Assertivo@0.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Assertivo&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=Assertivo&version=0.5.0
                    
Install as a Cake Tool

Assertivo

A fluent, strongly-typed assertion library for .NET with a .Should() entry-point pattern, zero-allocation happy paths, and AOT compatibility.

License: MIT Target Framework Build Status

Assertivo Logo

Disclaimer: Assertivo is a personal learning/playground project, built to explore .NET library design, AOT compatibility, source-generator-free fluent APIs, and Spec Kit capabilities. It is not affiliated with or endorsed by any commercial product, and is not recommended for production use without your own due diligence.

Quickstart

Add the using directive:

using Assertivo;

Write your first assertion:

[Fact]
public void My_first_assertion()
{
    int result = 2 + 2;
    result.Should().Be(4);
}

Run your tests:

dotnet test

Examples

Boolean assertions

bool isReady = true;
isReady.Should().BeTrue();

String assertions

string name = "Assertivo";
name.Should().Contain("Assert");
name.Should().NotBeEmpty();         // passes for any non-"" value, including null
name.Should().NotBeNullOrEmpty();   // fails on both null and ""
name.Should().NotContain("secret", "credentials must not be logged");
name.Should().NotBe("None");

Numeric assertions

int count = 10;
count.Should().BeGreaterThanOrEqualTo(5);
count.Should().BeGreaterThan(0);
count.Should().BeLessThan(100);
count.Should().BeLessThanOrEqualTo(10);
count.Should().NotBe(0);

// Chain comparisons in a single expression
count.Should().BeGreaterThan(0).And.BeLessThan(100);

Collection assertions

var items = new List<string> { "a", "b", "c" };
items.Should().HaveCount(3);
items.Should().Contain("b");
items.Should().BeEquivalentTo("c", "a", "b"); // order-independent
items.Should().NotBeEmpty();

var scores = new List<int> { 10, 20, 30 };
scores.Should().AllSatisfy(score => score.Should().BeGreaterThanOrEqualTo(10));
scores.Should().AllSatisfy((score, index) => score.Should().Be((index + 1) * 10));

Predicate collection containment

var orders = new[]
{
    new { Id = 1, Status = "Pending" },
    new { Id = 2, Status = "Shipped" }
};

// Passes when at least one element matches
orders.Should().Contain(o => o.Status == "Shipped");

// Supports chaining
orders.Should().Contain(o => o.Status == "Shipped").And.HaveCount(2);

Ordered sequence equality

// Inline values (params overload)
var result = GetSortedIds();
result.Should().Equal(10, 20, 30);

// IEnumerable overload with reason
var output = pipeline.Process(input);
output.Should().Equal(expectedOutput, because: "pipeline output must be deterministic");

// Custom comparer
var names = new[] { "Alice", "BOB" };
names.Should().Equal(new[] { "alice", "bob" }, comparer: StringComparer.OrdinalIgnoreCase);

Drill-down with .Which

var users = new List<User> { new("Alice") };
users.Should().ContainSingle()
    .Which.Name.Should().Be("Alice");

Exception assertions

Action act = () => throw new ArgumentNullException("param");
act.Should().Throw<ArgumentNullException>()
    .Which.ParamName.Should().Be("param");

Async exception assertions

Assert directly on a Task — no lambda wrapper required:

Task task = repository.SaveAsync(entity);
await task.Should().ThrowAsync<DbException>();

Chain further assertions on the caught exception via .Which:

Task<User> task = service.GetUserAsync(id);
var ex = await task.Should().ThrowAsync<NotFoundException>();
ex.Which.ResourceId.Should().Be(id);

Pass a because reason to provide context in CI output:

await task.Should().ThrowAsync<TimeoutException>(
    "because the call must fail fast when the upstream is unavailable");

The Func<Task> entry point is still supported for cases where you need to capture the subject lazily:

Func<Task> act = async () => { await Task.Delay(1); throw new InvalidOperationException(); };
await act.Should().ThrowAsync<InvalidOperationException>();

Async iterator assertions

Assert directly on an IAsyncEnumerable<T> — no await foreach, no try/catch, no lambda wrapper:

IAsyncEnumerable<int> stream = repository.StreamOrdersAsync(customerId);
await stream.Should().ThrowAsync<UnauthorizedException>();

Chain further assertions on the caught exception via .Which:

IAsyncEnumerable<Event> events = feed.ReadAsync();
var ex = await events.Should().ThrowAsync<StreamClosedException>();
ex.Which.Reason.Should().Be("server disconnected");

Pass a because reason to provide context in CI output:

await stream.Should().ThrowAsync<RateLimitException>(
    "because the quota for this tenant is exhausted");

AggregateException with a single inner exception is automatically unwrapped — the same behaviour as the Task and Func<Task> entry points:

// AggregateException(InvalidOperationException) → passes as InvalidOperationException
await stream.Should().ThrowAsync<InvalidOperationException>();

Object assertions

var a = new MyService();
var b = new MyService();

a.Should().NotBeSameAs(b);                                       // different instances
a.Should().NotBeSameAs(b, "each request must get its own scope");
a.Should().NotBeSameAs(b).And.NotBeNull();                       // chainable

Custom comparers

result.Should().Be(expected, StringComparer.OrdinalIgnoreCase);
items.Should().BeEquivalentTo(expected, myCustomComparer);

because reasons

logs.Should().NotContain("secret-key", "credentials must not be logged");
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.0 120 6/16/2026
0.4.0 118 6/1/2026
0.3.0 165 5/8/2026
0.2.0 128 4/26/2026
0.1.2 128 4/9/2026
0.1.1 114 4/7/2026