A pytest inspired assertion library for .NET with no special syntax.
SharpAssert provides rich assertion diagnostics by automatically transforming your assertion expressions at compile time using MSBuild source rewriting, giving you detailed failure messages with powerful expression analysis.
using static SharpAssert.Sharp;
var items = new[] { 1, 2, 3 };
var target = 4;
Assert(items.Contains(target));
// Assertion failed: items.Contains(target) at MyTest.cs:15
// items: [1, 2, 3]
// target: 4
// Result: false
- π Detailed Expression Analysis - See exactly why your assertions failed
- π¦ Simple Setup - Just add NuGet package, no MSBuild configuration needed
- π PowerAssert Integration - Complete support for PowerAssert (switch option)
dotnet add package SharpAssert
using static SharpAssert.Sharp;
[Test]
public void Should_be_equal()
{
var expected = 4;
var actual = 5;
Assert(expected == actual);
// Assertion failed: expected == actual
// Left: 4
// Right: 5
// Result: false
}
Assert(user.IsActive, $"User {user.Name} should be active for this operation");
using static SharpAssert.Sharp;
[Test]
public async Task Throws_catch_exceptions_in_exception_result()
{
// Thows returns ExceptionResult which allows using them as condition in Assert
Assert(Throws<ArgumentException>(()=> new ArgumentException("foo")));
Assert(Throws<ArgumentException>(()=> new ArgumentNullException("bar"))); // will throw unexpected exception
Assert(!Throws<ArgumentException>(()=> {})); // negative assertion via C# not syntax
var ex = Throws<ArgumentException>(()=> new ArgumentException("baz")); // always returns ExceptionResult
Assert(ex.Exception.ArgumentName == "baz"); // get thrown exception and assert on any custom property
Assert(Throws<ArgumentException>(()=>
new ArgumentException("baz")).Data == "baz"); // shortcut form to assert on exception Data property
Assert(Throws<ArgumentException>(()=>
new ArgumentException("bar")).Message.Contains("bar")); // shortcut form to assert on exception Message
// async version
Assert(await ThrowsAsync<ArgumentException>(()=>
Task.Run(() => throw ArgumentException("async"))));
var ex = ThrowsAsync<ArgumentException>(()=> Task.Run(() => throw ArgumentException("async"))); // always returns ExceptionResult
Assert(ex.Message.Contains("async")); // assert on message using shortcut on ExceptionResult
}
SharpAssert uses MSBuild source rewriting to automatically transform your assertion calls at compile time:
- You write:
Assert(x == y)
- MSBuild rewrites:
global::SharpAssert.SharpInternal.Assert(() => x == y, "x == y", "file.cs", 42)
- Runtime analysis: Expression tree provides detailed failure diagnostics when assertions fail
var order = new Order { Items = new[] { "Coffee", "Tea" }, Total = 15.50m };
var expectedTotal = 12.00m;
Assert(order.Items.Length > 0 && order.Total == expectedTotal);
// Assertion failed: order.Items.Length > 0 && order.Total == expectedTotal
// order.Items.Length > 0 β True (Length: 2)
// order.Total == expectedTotal β False
// order.Total: 15.50
// expectedTotal: 12.00
// Result: False
SharpAssert is built on modern .NET technologies:
- MSBuild Source Rewriting - Compile-time code transformation
- Roslyn Syntax Analysis - Advanced C# code parsing and generation
- Expression Trees - Runtime expression analysis
- CallerArgumentExpression - Fallback for edge cases
SharpAssert includes PowerAssert integration and also uses it as a fallback mechanism for not yet implemented features.
To force PowerAssert for all assertions:
<PropertyGroup>
<UsePowerAssert>true</UsePowerAssert>
</PropertyGroup>
- Warning about legacy RID used by PowerAssert (dependency). Fix by adding to project properties:
<!-- Suppress NETSDK1206 warning from PowerAssert's Libuv dependency -->
<NoWarn>$(NoWarn);NETSDK1206</NoWarn>
- Collection initializers could not be used in expression trees. Compiler limitation. Use
new[]{1,2,3}
instead of[1, 2, 3]
- Verify
SharpAssert
package is installed (SharpAssert.Runtime comes automatically) - Ensure
using static SharpAssert.Sharp;
import
- Check build output contains: "SharpAssert: Rewriting X source files"
- Verify rewritten files exist in
obj/Debug/net9.0/SharpRewritten/
- Ensure
SharpInternal.Assert
calls are being made (check generated code) - Look for #line directives in generated files
We welcome contributions! Please see our comprehensive Contributing Guide for:
- π Quick start guide for developers
- π§ͺ Testing strategy and workflow
- π¦ Package versioning best practices
- π§ Development tips and debugging help
- π Commit guidelines and release process