Why doesn't simple test pass using AutoFixture Freeze, SemanticComparison Likeness and CreateProxy?
Asked Answered
W

1

7

I'm trying to understand how to use the CreateProxy() feature of Likeness<T>() using two instances of a simple class.

public class Band
{
    public string Strings { get; set; }
    public string Brass { get; set; }
}

With the following test, I use a Fixture to Create<T> a Band instance with values for the two string properties.

[Fact]
public void Equality_Behaves_As_Expected()
{
    // arrange
    var fixture = new Fixture();
    fixture.Customize(new AutoMoqCustomization());

    var original = fixture.Create<Band>();
    //   Brass something like --> "Brass65756b89-d9f3-42f8-88fc-ab6de5ae65cd"
    //   Strings something like --> "Strings7439fa1b-014d-4544-8428-baea66858940"

    // act
    var dupe = new Band {Brass = original.Brass, 
                         Strings = original.Strings};
    //   Brass same as original's like --> "Brass65756b89-d9f3-42f8-88fc-ab6de5ae65cd"
    //   Strings same as original's like --> "Strings7439fa1b-014d-4544-8428-baea66858940"

I've tried many different assertions, but the crux of the matter seems to be that the CreateProxy method is not populating the properties of Band, so that even when I try to compare two instances of Band with the same property values, the instance from the CreateProxy method always has null values.

    // assert
    var likeness = dupe.AsSource().OfLikeness<Band>()
                       .Without(x => x.Brass).CreateProxy();
    //   Brass & String properties are null using dupe as source of likeness (!)

    //var likeness = original.AsSource().OfLikeness<Band>()
    //                       .Without(x => x.Brass).CreateProxy();
    //   Brass & String properties are null using original as source of likeness (!)

    //Assert.True(likeness.Equals(original)); // Fails
    //Assert.True(original.Equals(likeness)); // Fails

    // below are using FluentAssertions assembly
    //likeness.Should().Be(original);           // Fails (null properties)
    //original.Should().Be(likeness);           // Fails (null properties)
    //likeness.ShouldBeEquivalentTo(original);  // Fails (null properties)
    //original.ShouldBeEquivalentTo(likeness);  // Fails (null properties)
}

I've gotta be doing something wrong, but I've read everything I can find on the Ploeh blog and SO, and can't find an example suitably simple enough to compare to what I'm doing. Any ideas?

Whipcord answered 18/3, 2013 at 6:38 Comment(0)
D
5

If you assign the values on the proxied instance (after calling the CreateProxy method) the test passes:

[Fact]
public void Equality_Behaves_As_Expected()
{
    // AutoMoqCustomization is not necessary.
    var original = new Fixture().Create<Band>();

    var likeness = original
        .AsSource()
        .OfLikeness<Band>()
        .Without(x => x.Brass)
        .CreateProxy();

    likeness.Brass = "foo"; // Ignored.
    likeness.Strings = original.Strings;

    Assert.True(likeness.Equals(original));
    likeness.Should().Be(original);
    likeness.ShouldBeEquivalentTo(original);
}

Keep in mind that Likeness creates a proxy on the target type and only that type's instance overrides Equals.

Since the source type remains intact, the following assertions will not succeed:

Assert.True(original.Equals(likeness));
original.Should().Be(likeness);
original.ShouldBeEquivalentTo(likeness);

Update

From version 3.0.4 and above the values are automatically copied to the proxy instance (which means, likeness.Strings = original.Strings; is going to happen automatically).

Dextrorse answered 18/3, 2013 at 11:49 Comment(9)
But why does Assert.True(likeness.Equals(original)); fail? The OP states that this line of code also fails. Shouldn't that pass?Thenceforward
Because the values were not assigned on the proxied instance. Assert.True(likeness.Equals(original)); in the original test could pass if CreateProxy copied all properties from the source instance to the target instance.Dextrorse
@Nikos - ok, so one concept illustrated here is that the equality assertion isn't bi-directional (meaning, if one indirectly modifies the equality comparator of an object using the proxy, then the object's proxy's equality must be what's exercised - not the equality comparator of the object to which it's being compared (please feel free to edit this comment if I'm using the wrong terminology). Is that right?Whipcord
Yes. The equality assertion is symmetric for two proxied instances, which means that for proxies x and y x.Equals(y) returns the same value as y.Equals(x). However, since in the above test the source type isn't proxied only the proxied Equals can be exercised. Note that this allows you to perform very flexible comparisons.Dextrorse
@NikosBaxevanis There must be something I'm not at all getting. The likeness proxy is generated from dupe, and dupe has the same property values as original. I would expect likeness to override Equals to return true if the properties match (actually excluding Brass, so only Strings would have to match). Since original.Strings is equal to dupe.Strings I would expect the proxy generated from dupe to equal original... What is it that I'm missing?Thenceforward
@MarkSeemann, @Nikos - my intent is to use original as a sort of Object Mother for test data. The example is simple, in that I simply create dupe right in the test, but in a more complex example, it would be more like var dupe = sut.Create(original.Brass, original.Strings);. I'm not sure whether it's clearer to create the Likeness<T> using dupe or original as the source (.AsSource()) - any guidance there?Whipcord
@MarkSeemann CreateProxy does not copy the property values from the target type to the proxied type. (Once the proxy is created, one has to assign the property values and then do the comparison.)Dextrorse
That is, I believe, a bug. I've created github.com/AutoFixture/AutoFixture/issues/87 to track this.Thenceforward
Currently, there is work in progress so that the CreateProxy method can copy the public fields/properties. That way, there won't be necessary to assign the values on the proxied instance (after calling the CreateProxy method).Dextrorse

© 2022 - 2024 — McMap. All rights reserved.