Search code examples
c#unit-testingfluent-assertions

How to assert structural object equivalence in C# (with strict type equality)


How can you elegantly assert the following kind of equivalence between two .NET objects (ideally using the Fluent Assertions library)?

Two objects are structurally equivalent if:

  • both objects are of the same (run-time) type, and
  • the public properties of both objects are (recursively) structurally equivalent.

Note that subject.Should().BeEquivalentTo(expectation) does not work since BeEquivalentTo does not check type equality. For example, if we have two classes A and B each with a single property object X { get; set; }, then the two objects

new A { X = new B { X = new A() }}

and

new B { X = new A { X = new B() }}

would be deemed equivalent by BeEquivalentTo, even though their types and the types of their properties and subproperties do not match, and hence are not structurally equivalent by the above definition.


Solution

  • I came up with this solution, but I would have hoped there was a more elegant one.

    Define a custom IEquivalencyStep:

        public class StrictTypeEquivalence : IEquivalencyStep
        {          
            public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
            {
                return context.Subject != null && context.Expectation != null &&
                       context.Subject.GetType() != context.Expectation.GetType();
            }
    
            public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
            {
                throw new AssertionFailedException($"{context.SelectedMemberPath}: Expected type {context.Expectation.GetType()} but found {context.Subject.GetType()} instead.");
            }
        }
    

    And then check equivalence like so:

    subject.Should().BeEquivalentTo(expectation, options => options.Using(new StrictTypeEquivalence()));