FluentAssertions - Should().BeEquivalentTo() when properties are of different type
Asked Answered
F

1

5

How to compare objects that have a property with the same name but different type?

public class A
{
    public Guid Id { get; set; }
}

public class B
{
    public string Id { get; set; }
}

public static B Map(A a){
    return new B { Id = a.Id.ToString() };
}

Version 1:

void Main()
{
    A a = new A { Id = Guid.NewGuid() };
    B b = Map(a);

    b.Should().BeEquivalentTo(a);
}

This produces the following error:

AssertionFailedException: Expected member Id to be {ff73e7c7-21f0-4f45-85fa-f26cd1ecafd0}, but found "{ff73e7c7-21f0-4f45-85fa-f26cd1ecafd0}".

The documentation suggests that custom property assertion rules could be possible using the Equivalence Comparison Behavior

Version 2:

void Main()
{
    A a = new A { Id = Guid.NewGuid() };
    B b = Map(a);

    b.Should().BeEquivalentTo(a, 
        options => options
            .Using<Guid>(ctx => ctx.Subject.Should().Be(ctx.Expectation))
            .WhenTypeIs<Guid>());
}

but it produces run-time exception if the properties are not of the same type.

AssertionFailedException: Expected member Id from subject to be a System.Guid, but found a System.String.

Any ideas?

Fain answered 1/3, 2018 at 16:14 Comment(0)
F
11

Here are two approaches to compare objects with identically named members of different type.

The first way is to specify equivalency between members named Id

A expected = new A { Id = Guid.NewGuid() };
B actual = Map(expected);

actual.Should().BeEquivalentTo(expected,
    options => options
    .Using<object>(ctx => ctx.Subject.Should().Be(ctx.Expectation.ToString()))
    .When(info => info.SelectedMemberPath.EndsWith("Id")));

The second approach uses the DifferentObjectsEquivalencyStep from https://mcmap.net/q/2029478/-how-to-compare-two-collections-that-vary-by-properties-using-fluent-assertion and your own Map function. It then converts instances of A into B, which are now easy to compare.

AssertionOptions.AssertEquivalencyUsing(c => 
    c.Using(new DifferentObjectsEquivalencyStep<A, B>(Map)));

A expected = new A { Id = Guid.NewGuid() };
B actual = Map(expected);

actual.Should().BeEquivalentTo(expected);

There is an open issue about it.

Feverwort answered 1/3, 2018 at 17:29 Comment(2)
DifferentObjectsEquivalencyStep should be added to fluentassertions, as extension methods!Lillis
@MichalCiechan Feel free to open an issue about on the issue tracker. I'm currently fiddling with another approach where you'll be able to write .Using<string, Guid>(ctx => ctx.Subject.Should().Be(ctx.Expectation.ToString())).When(info => info.SelectedMemberPath.EndsWith("Id"))). That should be less fragile and more expressive.Feverwort

© 2022 - 2024 — McMap. All rights reserved.