yinnonsanders
yinnonsanders

Reputation: 1871

Comparing lists of objects with double property with FluentAssertions (C#)

I am trying to compare two list of objects with FluentAssertions. The objects have a property stored as a double that may be off by a small amount. Is there an efficient way to do this without iterating through the lists? My current method looks like

actualList.ShouldAllBeEquivalentTo(expectedList, options => options.Excluding(o => o.DoubleProperty));

for (var i = 0; i < actualList.Count; i++)
{
    actualList[i].DoubleProperty
                 .Should().BeApproximately(expectedList[i].DoubleProperty, precision);
}

Which is a little ugly and irritating as this issue keeps on coming up. Another possibility (inspired by Fluent Assertions: Compare two numeric collections approximately) is

actualList.Select(o => o.DoubleProperty)
          .Should().Equal(expectedList.Select(o => o.DoubleProperty),
                          (left, right) => AreEqualApproximately(left, right, precision));

Where I would write the AreEqualApproximately function myself. If possible, I would like to do the comparison without defining my own helper methods or iterating through the lists by index.

Upvotes: 2

Views: 1211

Answers (3)

yinnonsanders
yinnonsanders

Reputation: 1871

Based on Fluent Assertions: Approximately compare a classes properties

actualList.ShouldAllBeEquivalentTo(
    expectedList,
    options => options.Using<double>(d => d.Subject.Should().BeApproximately(d.Expectation, precision))
                      .WhenTypeIs<double>()

Turns out to work the best for me, although because I have to do this several times, I ended up changing the options for FluentAssertions globally in TestInitialize.

Upvotes: 1

Nkosi
Nkosi

Reputation: 247088

The following should also work using the options available in ShouldAllBeEquivalentTo

actualList.ShouldAllBeEquivalentTo(expectedList, options => options
    .Using<double>(ctx => ctx.Subject.Should()
                             .BeApproximately(ctx.Expectation, precision))
    .When(o => o.SelectedMemberPath == "DoubleProperty"));

Upvotes: 2

hoodaticus
hoodaticus

Reputation: 3880

You can create extension methods that will merge your actual and expected values into a single list and foreach over them:

public static class ExtensionMethods
{
    public static IEnumerable<ValueTuple<T, T>> Merge<T>(this List<T> a, List<T> b)
    {
        for (int x = 0, y = 0; x < a.Count && y < a.Count; x++, y++) 
        {
            yield return ValueTuple.Create(a[x], b[y]);
        }
    }

    public static void ForEach<T>(this IEnumerable<T> s, Action<T> m)
    {
       foreach (var i in s) m(i);
    }
}

Then, you can use it like this:

actualList.Merge(expectedList)
   .ForEach(i => 
   i.Item1.DoubleProperty
   .Should().BeApproximately(i.Item2.DoubleProperty, precision)); 

Upvotes: 1

Related Questions