fluentassertions icon indicating copy to clipboard operation
fluentassertions copied to clipboard

collection Should().BeEquivalentTo() vs position of first difference

Open mabead opened this issue 7 years ago • 2 comments

Description

I use the collection .Should().BeEquivalent() to ensure that two collections are equivalent. When my two collections do not have the same size, it is very hard to know at which index the first difference was found.

Complete minimal example reproducing the issue

Here's my test code:

class MyPair
{
    public int x { get; set; }
    public int y { get; set; }
    public override string ToString() => $"{x}-{y}";
}

[Fact]
public void TestBeEquivalent()
{
    var expected = Enumerable.Range(1, 100).Select(i => new MyPair { x = i, y = i + 20 }).ToList();

    // Make 'actual' almost identical to 'expected'
    var actual = new List<MyPair>(expected);
    actual.Insert(95, new MyPair { x = 0, y = 0 });

    actual.Should().BeEquivalentTo(expected);
}

Expected behavior:

I would expect the test to fail with a message similar to this that pin points where the two collections start to be non-equivalent:

The collections are identical until position 95 (0 based) where "0-0" is not equivalent to "96-116". 

Actual behavior:

The test fails with this message:

Expected actual to be a collection with 100 item(s), but {1-21, 2-22, 3-23, 4-24, 5-25, 6-26, 7-27, 8-28, 9-29, 10-30, 11-31, 12-32, 13-33, 14-34, 15-35, 16-36, 17-37, 18-38, 19-39, 20-40, 21-41, 22-42, 23-43, 24-44, 25-45, 26-46, 27-47, 28-48, 29-49, 30-50, 31-51, 32-52, …69 more…}"
"contains 1 item(s) more than"
"{1-21, 2-22, 3-23, 4-24, 5-25, 6-26, 7-27, 8-28, 9-29, 10-30, 11-31, 12-32, 13-33, 14-34, 15-35, 16-36, 17-37, 18-38, 19-39, 20-40, 21-41, 22-42, 23-43, 24-44, 25-45, 26-46, 27-47, 28-48, 29-49, 30-50, 31-51, 32-52, …68 more…}.

As you can see, it is very hard to know "why" the collections are not equivalent. Just looking at the output, it is impossible to know the error. I have to run the debugger, watch variables, copy paste the two collections in a diff tool and find the differences from there. It would be very hepfull if FluentAssertions could help identify where the collections start to diverge.

Versions

  • FluentAssertions: 5.5.3
  • .NET Core 2.1

mabead avatar Dec 20 '18 16:12 mabead

For anyone willing to pick up this task, be aware that comparing sequences with BeEquivalentTo can compare collections in either WithStrictOrdering() or WithoutStrictOrdering(), where the latter is the default. The difference in strictness should be reflected in the failure message, as only strict ordering requires the sequence elements to match index by index.

Successful assertions should execute fast and failing assertions should provide good failure messages.

As pointed out, including everything in the failure message doesn't necessarily make a great failure message as it may hide the valuable information in the data. So the striking question for me here is, what information makes value to include in the failure message. Is it valuable to include all the matching elements or even any of the matching elements? If it does make sense to include some matching elements, could we at least do a more intelligent pruning to highlight the mismatch(es). E.g. only include one or more preceding and succeeding elements of the mismatch in the failure message.

Here are some sketches on how I think pruned failure message could look like.

WithoutStrictOrdering

var subject = new [] { 1, 2, 3, 4, 7, 8, 9, 42 };
var expected = new [] { 1, 2, 4, 5, 7, 8, 9, 43 };

subject.Should().BeEquivalentTo(expected, o => o.WithoutStrictOrdering());
Expected
{ ..., 2, 3, 4, ..., 9, 42} 
to be equivalent to
{ ..., 2, 4, 5, ..., 9, 43}, 
but found no equivalents found for "3" (2) and "42" (7) in subject and "5" (3) and "43" (7) in expected.

WithStrictOrdering

var subject = new [] { 1, 2, 3, 4, 5, 6, 7 };
var expected = new [] { 1, 2, 42, 4, 5, 6, 43 };

subject.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering());
Expected
{ ..., 2, 3, 4, ..., 6, 7}
to be equivalent to
{ ..., 2, 42, 4, ..., 6, 43}, 
but found mismatches between "3" (2) and "42" and "7" (6) and "43".

jnyrup avatar Feb 02 '19 21:02 jnyrup

Those are good acceptance criteria @jnyrup

dennisdoomen avatar Feb 03 '19 07:02 dennisdoomen