-
Notifications
You must be signed in to change notification settings - Fork 731
Closed
Description
Description
If I want to check for multiple conditions that I all want to report, and one happens to be a check on the existence of an item in a collection (and using .Which for additional checking in the item itself if it does exist), I do not expect .Which to throw if the Which Constraint<,> failed, but store the failure in the AssertionScope and ignore the constraint on .Which instead.
Reproduction Steps
// given similar API:
public class MyDataReader : IDataReader
{
IEnumerable<IField> Fields { get; set;}
IField GetField(string name) => Fields.SingleOrDefault(f=>f.ColumnName == name);
// (...)
}
public class Field : IField
{
Field (string name, object value)
{
ColumnName = name;
DataValue = value;
}
string ColumnName { get; }
object DataValue { get; }
}
// Arrange
// values are both incorrect (swapped), column-/fieldname "Name" is different from expected "LastName"
var myRecord = new Field[] { new("FirstName", "Doe"), new("Name", "John") };
var dr = new MyDataReader { Fields = myRecord };
string input = "MyString";
// Act
bool result = dr.Read();
// Assert
result.Should().BeTrue();
using (new AssertionScope)
{
// Expected "LastName", but actual is "Name"
dr.Fields.Should().ContainSingle(field=>field.ColumnName=="LastName").Which.DataValue.Should().Be("Doe");
// Because .Which above throws, the below code is not executed, but its results are expected to be included in the output.
dr.Fields.Should().ContainSingle(field=>field.ColumnName=="FirstName").Which.DataValue.Should().Be("John"); // <== Mismatches found here are not in the output
}Expected behavior
Expected dr.Fields to contain a single item matching (field.ColumnName == "LastName"), but no such item was found.
Expected dr.Fields to be "John" with a length of 4, but "Doe" has a length of 3, differs near "Doe" (index 0).
// actual, the subject description for the check on .Which ('dr.Field') could also be improved ('dr.Fields.Item.DataValue'(?)), but I think there is already another issue for that. (related to #1502 and/or #2253 ?)
Actual behavior
Expected dr.Fields to contain a single item matching (field.ColumnName == "LastName"), but no such item was found.
Regression?
No response
Known Workarounds
using (new AssertionScope)
{
// Expected "LastName", but actual is "Name"
using (var inner = new AssertionScope()) // isolated inner scope (in case there is an assertion added above that resulted in a failure)
if (dr.Fields.Should().ContainSingle(field=>field.ColumnName=="LastName") is var constraint && !inner.HasFailures())
constraint.Which.DataValue.Should().Be("Doe");
// Because .Which above is now only accessed if there is no failure, it will not throw, so the below code is executed, and results are included in the output.
// Has no effect here now, but there may be assertions added below in the future
using (var inner = new AssertionScope()) // isolated inner scope (so we now hasFailure would always be caused by this assertion)
if (dr.Fields.Should().ContainSingle(field=>field.ColumnName=="FirstName") is var constraint && !inner.HasFailures())
constraint.Which.DataValue.Should().Be("John"); // <== Mismatches found here are now in the output
}or
using (var scope = new AssertionScope)
{
// using extension (A)
if (scope.IsSuccessful(() => dr.Fields.Should().ContainSingle(field => field.ColumnName == "LastName")
// dr.GetField("LastName").DataValue.Should().Be("Doe");
dr.Fields.Single(field => field.ColumnName == "LastName").DataValue.Should().Be("Doe");
// (...)
// or (B)
scope.IfSuccessful(() => dr.Fields.Should().ContainSingle(field => field.ColumnName == "LastName")
field => field.DataValue.Should().Be("Doe"));
// _ => dr.Fields.Single(field => field.ColumnName == "LastName").DataValue.Should().Be("Doe"); // would work too and adds some more context to the output.
// (...)
}
public static class FluentAssertionExtensions
{
// extending on AssertionScope to reduce chance to have them apply in code where they are not relevant
// alternative A
public static bool IsSuccessful(this IAssertionScope _ , Action evaluateConstraint)
{
using var scope = new AssertionScope();
evaluateConstraint();
return !scope.HasFailures();
}
// alternative B
public static void IfSuccessful<TParent, T>(this IAssertionScope _ , Func<AndWhichConstraint<TParent,T>> constraintFunc, Action<T> evaluateItem)
{
using var scope = new AssertionScope();
var constraint = constraintFunc();
if (!scope.HasFailures())
evaluateItem(constraint.Which);
}
// alternative C (quick and dirty - use of exception for control flow)
public static void IfSuccessful<TParent, T>(this AndWhichConstraint<TParent, T> constraint, Action<T> code)
{
T subject;
try { subject = constraint.Which!; }
catch (Exception) { return; }
code(subject);
}
}Configuration
No response
Other information
No response
Are you willing to help with a pull-request?
No