Search code examples
c#.net-corenunit

Why does Assert.Multiple 'remember' an already caught assertion failure?


The following code:

using NUnit.Framework;

namespace Sandbox {
    public class Program {
        public static void Main(string[] args) {
            try {
                Assert.Fail("Fail!");
            } catch (Exception e) {
                Console.WriteLine("Expected exception");
            }

            try {
                Assert.Multiple(() => { Assert.True(true); });
            } catch (Exception e) {
                Console.WriteLine("Unexpected exception: " + e.Message);
            }
        }
    }
}

prints

Expected exception
Unexpected exception: Fail!

Why is the AssertionException, which is caught in the first block, picked up a second time by Assert.Multiple? If I just do Assert.True(true);, no exception is thrown.

I don't think it matters, but I'm using .NET 6.0.


Solution

  • In (very) old versions, NUnit used an exception in order to (1) report errors and then (2) terminate the tests for the current test class.

    Back in 2016, however, these two functions were separated. This had to be done in order to support Assert.Multiple and to a lesser extent the warning asserts Warn.If and Warn.Unless. Those features require execution to be continued after the error is reported.

    It should be noted that users have been advised to avoid catching NUnit exceptions for about 20 years. That's because those exceptions are an internal implementation detail, subject to change. I should say that the NUnit developers have been giving that advice for many years. Other people, unfortunately, have often published code that involved catching them. :-) Anyway, six or seven years ago the inevitable happened: that implementation detail changed.

    It's a general principal that you should never try to handle or suppress an exception unless you understand what it means. The meaning of the assertion thrown by a test failure is (now) that the error has been recorded and the fixture is about to be terminated.

    That answers your "Why" question but most likely isn't very satisfying. :-)

    I suspect this may be an XY problem. Perhaps you might post another question about exactly what you are trying to accomplish and we could have another go at it.