开发者

NUnit, is it possible to continue executing test after Assert fails?

开发者 https://www.devze.com 2022-12-30 02:23 出处:网络
In a test that contains some asserts, for example: Assert.AreEqual(1,1); Assert.AreEqual(2,1); Assert.AreEqual(2,2);

In a test that contains some asserts, for example:

Assert.AreEqual(1,1);
Assert.AreEqual(2,1);
Assert.AreEqual(2,2);

is it possible to let the test keep running after it fails at some point? In the example, first condition is true, second fails and the test stops. I'd like to 开发者_Go百科evaluate also the following condition.


I prefer to be practical and put several related assertions in one method.

I have a helper class which enables the following syntax (I use):

AssertAll.Succeed(
    () => Assert.AreEqual("bb", id.Context),
    () => Assert.AreEqual("abc", id.FullName),
    () => Assert.AreEqual("b", id.SessionID));

which gives me error messages like this:

Assert.AreEqual failed. Expected:<bb>. Actual:<b\c>. 
Assert.AreEqual failed. Expected:<abc>. Actual:<[b\c]a{103}>. 
at FXP_COM.Tests.EnumToStringConverterterTests.<>c__DisplayClass3.<ShouldConvert>b__0() in UnitTest1.cs: line 31
at FXP_COM.Tests.AssertAll.Succeed(Action[] assertions) in UnitTest1.cs: line 46 at FXP_COM.Tests.AssertAll.Succeed(Action[] assertions) in UnitTest1.cs: line 62
at FXP_COM.Tests.EnumToStringConverterterTests.ShouldConvert() in UnitTest1.cs: line 30

and the helper class is the following:

using System;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public static class AssertAll
{
    public static void Succeed(params Action[] assertions)
    {
        var errors = new List<Exception>();

        foreach (var assertion in assertions)
            try
            {
                assertion();
            }
            catch (Exception ex)
            {
                errors.Add(ex);
            }

        if (errors.Any())
        {
            var ex = new AssertionException(
                string.Join(Environment.NewLine, errors.Select(e => e.Message)),
                errors.First());

            // Use stack trace from the first exception to ensure first
            // failed Assert is one click away
            ReplaceStackTrace(ex, errors.First().StackTrace);

            throw ex;
        }
    }

    static void ReplaceStackTrace(Exception exception, string stackTrace)
    {
        var remoteStackTraceString = typeof(Exception)
            .GetField("_remoteStackTraceString",
                BindingFlags.Instance | BindingFlags.NonPublic);

        remoteStackTraceString.SetValue(exception, stackTrace);
    }
}


NUnit 3.6 adds Assert.Multiple method and MultipleAsserts attribute.

NUnit, is it possible to continue executing test after Assert fails?

See https://docs.nunit.org/articles/nunit/writing-tests/assertions/multiple-asserts.html


No. Typically in this situation you would put all the code above the asserts into a setup method, then write each assert into its own test case.


No you can't do it with NUnit alone. You have to do something like @Konstantin Spirin said. I created a small extension that you can use; it's called NUnit-GroupAssert. It can be found here: https://github.com/slvnperron/NUnit-GroupAssert

How to use it:

[Test]
public void Verify_GroupsExceptions()
{
    var group = new AssertGroup();
    group.Add(() => Assert.AreEqual(10, 20));
    group.Add(() => Assert.AreEqual(1, 1));
    group.Add(() => Assert.AreEqual(3, 4));
    group.Add(() => Assert.IsTrue(1 > 3));
    group.Verify();
}

// OR

public void Verify_GroupsExceptions()
{
    // Verifies on disposal
    using (var group = new AssertGroup())
    {
        group.Add(() => Assert.AreEqual(10, 20));
        group.Add(() => Assert.AreEqual(1, 1));
        group.Add(() => Assert.AreEqual(3, 4));
        group.Add(() => Assert.IsTrue(1 > 3));
    }
}

it will output:

Test failed because one or more assertions failed:
1) Expected: 10
But was: 20
From Verify_GroupsExceptions at line 18

2) Expected: 3
But was: 4
From Verify_GroupsExceptions at line 20

3) Expected: True
But was: False
From Verify_GroupsExceptions at line 21


You could restructure your test to wrap the assertions in try/catch block and keep track of them for later validation. I don't recommend this, however. You really should be using separate tests for each condition if you want them to be tested independently.

  bool[] assertionSuccesses = new bool[] { false, false, false };

  try
  {
       Assert.AreEqual( 1, 1 );
       assertionSuccesses[0] = true;
  }
  catch (AssertionException) {}
  ...

  if (assertionSuccesses.Any( s => !s ))
  {
       Assert.Fail("one of the assertions failed");
  }


You can cheat a little and not actually fail at a given point, but rather mark for failure, then fail at the very end, something like the following:

var sbError = new StringBuilder();
if (!SomeCondition()) {
  sbError.AppendLine("SomeCondition failed");
}
if (!SomeOtherCondition()) {
  sbError.AppendLine("SomeOtherCondition failed");
}
Assert.AreEqual(0, sbError.Length, sbError.ToString());

I wouldn't recommend this, but if you need to do it once or twice, it shouldn't be that bad.


Nope. You shouldn't really have more than one assert per test anyway, that reduces the seperation and makes it more difficult to find out which one failed.

If you have a lot of code that needs executing before the Assert, seperate it out into a [SetUp] function, or make it a seperate procedure.


Asserts thrown an NUnit.Framework.AssertionException if they fail. You could catch that exception on the second assert, evaluate the third assert, then re-throw the exception.

Not something I'd recommend, though, for the reasons pointed-out by Ed Woodcock and Carl Manaster.


I rewrote Konstantin Spirin's code sample into VB.

Imports NUnit.Framework
Imports System.Reflection

Public Class Group_LIB_NUnit_Assert_Multiple

    Public Shared Sub Multiple(ParamArray Assertions As Action())
        Dim ExceptionObj As Exception
        Dim Exceptions As New List(Of Exception)
        Dim Message As String

        For Each Assertion In Assertions
            Try
                Assertion()
            Catch ex As Exception
                Exceptions.Add(ex)
            End Try
        Next

        If Exceptions.Count > 0 Then
            Message = String.Format("{0}{1} assertions failed: {2}{3}", Environment.NewLine, Exceptions.Count, Environment.NewLine, String.Join(Environment.NewLine, Exceptions.Select(Function(e) e.Message).ToList()))

            ExceptionObj = New AssertionException(Message)

            StackTraceReplace(ExceptionObj, Exceptions.First.StackTrace)

            Throw ExceptionObj
        End If
    End Sub

    Public Shared Sub StackTraceReplace(ExceptionObj As Exception, StackTrace As String)
        Dim RemoteStackTraceString As FieldInfo

        RemoteStackTraceString = GetType(Exception).GetField("_remoteStackTraceString", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)

        RemoteStackTraceString.SetValue(ExceptionObj, StackTrace)

    End Sub
End Class

Imports Group.Library4
Imports NUnit.Framework

<TestFixture()> _
Public Class TEST_Group_LIB_NUnit_Assert_Multiple

    <Test()> _
    <TestCaseSource("Factory")> _
    Public Sub Multiple(TestObj As TEST_DATA_Group_LIB_NUnit_Assert_Multiple)

        Group_LIB_NUnit_Assert_Multiple.Multiple(Sub() Assert.That(TestObj.Gender, [Is].EqualTo("F"c), "Gender"), Sub() Assert.That(TestObj.Name, [Is].EqualTo("George Washington"), "Name"))
    End Sub

    Public Function Factory() As List(Of TEST_DATA_Group_LIB_NUnit_Assert_Multiple)
        Dim L As New List(Of TEST_DATA_Group_LIB_NUnit_Assert_Multiple)
        Dim TestObj As TEST_DATA_Group_LIB_NUnit_Assert_Multiple

        TestObj = New TEST_DATA_Group_LIB_NUnit_Assert_Multiple()
        TestObj.DOB = New DateTime(2015, 8, 12)
        TestObj.Gender = "M"c
        TestObj.Name = "Abraham Lincoln"
        L.Add(TestObj)

        TestObj = New TEST_DATA_Group_LIB_NUnit_Assert_Multiple()
        TestObj.DOB = New DateTime(2015, 8, 12)
        TestObj.Gender = "F"c
        TestObj.Name = "George Washington"
        L.Add(TestObj)

        TestObj = New TEST_DATA_Group_LIB_NUnit_Assert_Multiple()
        TestObj.DOB = New DateTime(2015, 8, 12)
        TestObj.Gender = "A"c
        TestObj.Name = "John Hancock"
        L.Add(TestObj)

        Return L
    End Function
End Class

Public Class TEST_DATA_Group_LIB_NUnit_Assert_Multiple

    Public Property DOB As Date

    Public Property Gender As Char

    Public Property Name As String

End Class
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号