Skip to content

Add AssertionResult.Failed overload that accepts an Exception #5381

@thomhurst

Description

@thomhurst

Problem Statement

When inline/nested assertions fail (e.g. inside WithInnerExceptions, Count(c => c.IsEqualTo(5)), All().Satisfy()), the failure is caught and converted to an AssertionResult.Failed(string). This loses the original exception's structured information — stack trace, inner exceptions, and any typed data on the AssertionException.

Currently there are 12+ sites in the codebase that use this pattern:

try
{
    await resultingAssertion.AssertAsync();
}
catch
{
    return AssertionResult.Failed("some message");
}

The original exception is discarded, making it harder for users to diagnose why a nested assertion failed.

Proposed Solution

Add an Exception property to AssertionResult and a new Failed overload:

public readonly struct AssertionResult
{
    public bool IsPassed { get; }
    public string Message { get; }
    public Exception? Exception { get; }

    public static AssertionResult Failed(string message, Exception? exception = null)
        => new(false, message, exception);
}

Then update Assertion<T>.ExecuteCoreAsync / CreateException to surface the inner exception when rendering failure output, and update the 12+ inline assertion catch sites to pass through the caught exception.

Benefits

  • Users get full error context when nested assertions fail
  • Stack traces are preserved for debugging
  • Consistent with how .NET surfaces inner failures (e.g. AggregateException.InnerExceptions)

Context

Discovered while implementing #5345 (WithInnerExceptions). The bare catch pattern is consistent with the existing codebase but loses valuable diagnostic information.

Feature Category

Assertions

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions