Fluent Assertions: Approximately compare a classes properties
Asked Answered
A

1

18

I have a class Vector3D that has the properties X, Y and Z of type double (it also has other properties such as Magnitude).

What is the best way of approximately comparing all the properties or a selection of the properties at a given precision using Fluent Assertions?

Currently I have been doing it like this:

calculated.X.Should().BeApproximately(expected.X, precision);
calculated.Y.Should().BeApproximately(expected.Y, precision);
calculated.Z.Should().BeApproximately(expected.Z, precision);

Is there a single line approach that will achieve the same thing? Such as using ShouldBeEquivalentTo, or does this require constructing a generic extension method that allows properties to be included / excluded?

Ashok answered 22/4, 2016 at 1:9 Comment(0)
G
31

Yes it's possible using ShouldBeEquivalentTo. The following code will check all properties that are of type double with a precision of 0.1 :

double precision = 0.1;
calculated.ShouldBeEquivalentTo(expected, options => options
    .Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, precision))
    .WhenTypeIs<double>());

If you want to compare only the X, Y and Z properties change the When constraint like this :

double precision = 0.1;
calculated.ShouldBeEquivalentTo(b, options => options
    .Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, precision))
    .When(info => info.SelectedMemberPath == "X" ||
                  info.SelectedMemberPath == "Y" ||
                  info.SelectedMemberPath == "Z"));

Another approach is to explicitly tell to FluentAssertions which properties should be compared, but it's a bit less elegant :

double precision = 0.1;
calculated.ShouldBeEquivalentTo(b, options => options
    .Including(info => info.SelectedMemberPath == "X" ||
                       info.SelectedMemberPath == "Y" ||
                       info.SelectedMemberPath == "Z")
    .Using<double>(ctx => ctx.Subject.Should().BeApproximately(ctx.Expectation, precision))
    .When(info => true));

Since the Using statement does not return a EquivalencyAssertionOptions<T> we need to hack it by calling the When statement with an always true expression.

Glimmering answered 22/4, 2016 at 1:35 Comment(5)
That's neat. So .When has to be used in this particular case instead of .Including to specify which properties are used for the equivalence test?Ashok
Including is to explicitly tell to FluentAssertions wich properties to use during the comparison. I have updated my answer with an explaination.Glimmering
I'm guess it is a matter of preference, but is there any disadvantage to using .Including(info => info.X).Including(info => info.Y).Including(info => info.Z) over the info.SelectedMemberPath == "X"...?Ashok
The overload you point allow you to check at compile time the correctness of your expression, but it is a bit more verbose.Glimmering
Very helpful answer! Is there a solution if double is also nullable?Fornication

© 2022 - 2024 — McMap. All rights reserved.