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.
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 182) Expected: 3
But was: 4
From Verify_GroupsExceptions at line 203) 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
精彩评论