Using NUnit 4.2, .NET 8
I can output my own custom exception message when it fails by using the message parameter in Assert.That(). This message gets displayed when the assert fails.
However, i'm looking for a way to output some messages even when the assert is a success. My use-case is that I have a lot of asserts in a single test method. And I'd like to be able to track what is being asserted in each test method from the CI pipeline.
TestContext.Out.WriteLine works to output logs even on success, I thought i'd extend the Assert.That method so that I don't have a bunch of TestContext.Out.WriteLines after every Assert, but I can't seem to get it to work. (C# skill issues)
[Test]
public void Add_Two_Numbers()
{
    int a = 2, b = 3, c = 4, d = 5;
    int resultAB = a + b;
    int resultCD = c + d;
    Assert.Multiple(() =>
    {
        Assert.That(resultAB, Is.EqualTo(5), message: "Somehow it's not 5", assertDescription: $"Checking if {a}+{b} is equal to 5");
        Assert.That(resultCD, Is.EqualTo(9), message: "Somehow it's not 9", assertDescription: $"Checking if {c}+{d} is equal to 9");
    });
}
Questions:
Assert.That with a new parameter.Assert.Multiple() scoped since, that doesn't throw exceptions.Note: Breaking it down to multiple Test methods is not an option for my particular use-case.
Assert. This will satisfy your proposed usage:using System.Runtime.CompilerServices;
using NUnit.Framework.Constraints;
public class Assert : NUnit.Framework.Assert
{
  public static void That<T>(
    T actual,
    IResolveConstraint expression,
    string message,
    string assertDescription,
    [CallerArgumentExpression(nameof(actual))] string actualExpression = "",
    [CallerArgumentExpression(nameof(expression))] string constraintExpression = "")
  {
#pragma warning disable NUnit2050 // False warning when we specify actualExpression & constraintExpression
    That(actual, expression, message, actualExpression: actualExpression, constraintExpression: constraintExpression);
#pragma warning restore NUnit2050
    TestContext.Out.WriteLine(assertDescription);
  }
}
public class Tester {
  [Test] public void Test1() {
    int a = 0, b = 3, c = 4, d = 5;
    Assert.Multiple(() => {
      Assert.That(a + b, Is.EqualTo(5), message: "Somehow it's not 5", assertDescription: $"Checking if {a}+{b} is equal to 5");
      Assert.That(c + d, Is.EqualTo(9), message: "Somehow it's not 9", assertDescription: $"Checking if {c}+{d} is equal to 9");
    });
  }
}
private.using System.Reflection;
using System.Runtime.CompilerServices;
using NUnit.Framework.Constraints;
public class Assert : NUnit.Framework.Assert
{
  private static readonly MethodInfo? ReportFailureMethod;
  static Assert() // Static constructor
  {
    // The ReportFailure() method is a private, 
    // so we need to backdoor it via Reflection.
    ReportFailureMethod = typeof(NUnit.Framework.Assert).GetMethod(
      "ReportFailure",
      BindingFlags.NonPublic | BindingFlags.Static,
      [typeof(ConstraintResult), typeof(string), typeof(string), typeof(string)]);
  }
  public static void That<T>(
    T actual,
    IResolveConstraint expression,
    string message,
    string assertDescription,
    [CallerArgumentExpression(nameof(actual))] string actualExpression = "",
    [CallerArgumentExpression(nameof(expression))] string constraintExpression = "")
  {
    // Emulate what the actual Assert.That() does:
    var constraint = expression.Resolve();
    var result = constraint.ApplyTo(actual);
    
    if (result.IsSuccess)
      TestContext.Out.WriteLine($"Success: {assertDescription}");
    else
      ReportFailureMethod?.Invoke(null, [result, message.ToString(), actualExpression, constraintExpression]);
  }
}
Unfortunately the above messes the stack trace logging. But it's still the closest we could get to emulating the original Assert.That() while catching a successful assert.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With