2

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.

7
  • 2
    What's ugly about it? I mean, other than not having newline sequences to break up the dot chain? Commented Jul 21, 2017 at 16:37
  • @hoodaticus In C#, I really shouldn't be iterating through lists by index. Beauty in code is something you really just have to sense. Commented Jul 21, 2017 at 16:47
  • 1
    You would need to transform your source data into a list of ValueTuples the first item of which is the actualList member and the second of which is the corresponding expectedList member. Then you would be able to do a List<ValueTuple<blah, blah>>.ForEach(i => i.Item1.DoubleProperty .Should().BeApproximately(i.Item2.DoubleProperty, precision)); The reason your code in its current form requires for loops by index is because it is having to manually correlate two separate lists. Put them in the same list and you are good. Commented Jul 21, 2017 at 16:52
  • 1
    If you make that an answer I'll accept it. Commented Jul 21, 2017 at 16:58
  • 1
    Take a look at this stackoverflow.com/questions/36782975/… Commented Jul 21, 2017 at 17:06

3 Answers 3

2

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"));
Sign up to request clarification or add additional context in comments.

Comments

1

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)); 

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.