3

In an earlier question I asked about Autofixture's CreateProxy method, a potential bug was identified.

I don't think this failing test is as a result of that, but rather my continued confusion about how the Likeness.Without(...).CreateProxy() syntax works. Consider the following failing test in which I make the original test ever so slightly more complex by creating a new instance of the object, considering its creation to be the SUT:

[Fact]
public void Equality_Behaves_As_Expected()
{
    // arrange: intent -> use the fixture-created Band as Object Mother
    var template = new Fixture().Create<Band>();

    // act: intent -> instantiated Band *is* the SUT
    var createdBand = new Band {Brass = template.Brass,
                                Strings = template.Brass};

    //   intent -> specify that .Brass should not be considered in comparison 
    var likeness = template.AsSource().OfLikeness<Band>().
        Without(x => x.Brass).CreateProxy(); // Ignore .Brass property

    // per [https://stackoverflow.com/a/15476108/533958] explicity assign
    // properties to likeness
    likeness.Strings = template.Strings;
    likeness.Brass = "foo"; // should be ignored

    // assert: intent -> check equality between created Band & template Band
    //         to include all members not excluded in likeness definition
    likeness.Should().Be(createdBand);          // Fails
    likeness.ShouldBeEquivalentTo(createdBand); // Fails
    Assert.True(likeness.Equals(createdBand));  // Fails
}

Here's the Band:

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

My earlier question wasn't sufficiently complex to help me understand what the Source of the Likeness should be in general.

Should the source be the output of the SUT, in which case it would be compared to the template instance created by AutoFixture?

Or should the source be the template instance created by AutoFixture, in which case it would be compared to the output of the SUT?

EDIT: Corrected an error in the test

I realized that I had incorrectly assigned the template.Brass property to both the Brass and the Strings property of the new Band instance. The updated test reflects the correction with var createdBand = new Band {Brass = template.Brass, Strings = template.Strings} and all six assertions pass now.

[Fact]
public void Equality_Behaves_As_Expected()
{
    // arrange: intent -> use the fixture-created Band as Object Mother
    var template = new Fixture().Create<Band>();

    // act: intent -> instantiated Band *is* the SUT
    var createdBand = new Band {Brass = template.Brass, Strings = template.Strings};

    // likeness of created
    var createdLikeness = createdBand.AsSource().OfLikeness<Band>().
        Without(x => x.Brass).CreateProxy(); // .Brass should not be considered in comparison 

    // https://stackoverflow.com/a/15476108/533958 (explicity assign properties to likeness)
    createdLikeness.Strings = createdBand.Strings;
    createdLikeness.Brass = "foo"; // should be ignored

    // likeness of template
    var templateLikeness = template.AsSource().OfLikeness<Band>()
        .Without(x => x.Brass)
        .CreateProxy();
    templateLikeness.Strings = template.Strings;
    templateLikeness.Brass = "foo";

    // assert: intent -> compare created Band to template Band
    createdLikeness.Should().Be(template);
    createdLikeness.ShouldBeEquivalentTo(template);
    Assert.True(createdLikeness.Equals(template));

    templateLikeness.Should().Be(createdBand);
    templateLikeness.ShouldBeEquivalentTo(createdBand);
    Assert.True(templateLikeness.Equals(createdBand));
}
Community
  • 1
  • 1
Jeff
  • 2,191
  • 4
  • 30
  • 49
  • About explicitly assigning properties, [this is going to be improved on the next versions](http://stackoverflow.com/questions/15470997/why-doesnt-simple-test-pass-using-autofixture-freeze-semanticcomparison-likene#comment22060162_15476108). – Nikos Baxevanis Mar 22 '13 at 07:24
  • @NikosBaxevanis Thank you for the work on that! Until then, I've done what you suggested in your answer to my previous question, which is to explicitly assign the property values to what I expect should be in the instance with which I am going to check equality, except for any properties excluded from the likeness using the .Without(...) syntax. Do I have that part right? – Jeff Mar 22 '13 at 17:04
  • @Lumiris Yes, that is correct. – Nikos Baxevanis Mar 22 '13 at 19:52
  • From version 3.0.4 and above the values are automatically copied to the proxy instance (which means, `createdLikeness.Strings = createdBand.Strings;` is going to happen automatically) – Nikos Baxevanis Mar 28 '13 at 08:53

1 Answers1

3

What you mean is:

likeness.Should().BeAssignableTo<Band>(); // Returns true.

In the example provided, the proxy generated from Likeness is a type deriving from Band, overriding Equals using the Semantic Comparison algorithm.

Using Reflection that is:

createdBand.GetType().IsAssignableFrom(likeness.GetType()) // Returns true.

Update:

The createBand and template instances are not affected by the CreateProxy method. Why they should?

With Likeness CreateProxy you basically create a Custom Equality Assertion that allows you to do:

Assert.True(likeness.Equals(createdBand)); // Passed. 

Without it, the original Equality Assertion would fail:

Assert.True(template.Equals(createdBand)); // Failed.

However, the following will also fail:

Assert.True(likeness.Equals(template));

It fails because the Strings value is the one from the createdBand instance.

This behavior is expected, and you can verify it using Likeness directly:

 createdBand.AsSource().OfLikeness<Band>()
     .Without(x => x.Brass).ShouldEqual(template);

Output:

 The provided value `Band` did not match the expected value `Band`. The following members did not match:
      - Strings.
Nikos Baxevanis
  • 10,868
  • 2
  • 46
  • 80
  • Isn't that just comparing types? I want to compare the likeness (created from the template) to the createdBand (created from the SUT) for equality (all members except for those excluded specifically in the likeness creation). I'm confident enough that they'll be of the same type or assignable to the same type that I don't want to verify that explicitly. I'll try to update the question to be more clear about that. – Jeff Mar 22 '13 at 16:57
  • @Lumirris In the updated test, you have to compare the `Likeness` proxy (destination) with the *template* instance (source). – Nikos Baxevanis Mar 22 '13 at 20:26
  • If I update to `likeness.Should().Be(template)`, test passes, but it's not examining the SUT output (`createdBand`). But if `createdBand` is used as source of likeness (`var likeness = createdBand.AsSource().OfLikeness().Without(x => x.Brass).CreateProxy()` and `likeness.Strings = createdBand.Strings`) then the SUT's output is referenced through the likeness, and the tests fail again. I think the likeness must be composed completely from `createdBand` or completely from `template`, and either way the test fails. Sorry if I'm not getting something obvious - I'm really trying to get it. – Jeff Mar 22 '13 at 22:13
  • @Lumirris AFAICT [the test passed](https://gist.github.com/moodmosaic/5225340#file-gistfile1-txt).. – Nikos Baxevanis Mar 22 '13 at 22:50
  • @Lumirris `CreateProxy` generates a type deriving from `TDestination` overriding `Equals`.. Perhaps a blog post [is on the way](https://twitter.com/nikosbaxevanis/status/315230055221567488) soon though. – Nikos Baxevanis Mar 22 '13 at 22:57
  • I left [a comment](https://gist.github.com/moodmosaic/5225340) on your gist. It doesn't look like any equality assertions are made between the template instance and the createdBand instance. It looks to me like your assertions are only testing the Likeness & CreateProxy() functionality, not the output of the SUT. What am I not seeing? – Jeff Mar 23 '13 at 16:54
  • @Lumirris I left a comment there. – Nikos Baxevanis Mar 24 '13 at 05:56
  • I [replied](https://gist.github.com/moodmosaic/5225340) to your comment. Sorry, not sure if you are automatically notified of comments on your gist. – Jeff Mar 24 '13 at 18:13
  • See my latest edit to the question. @NikosBaxevanis your answers/comments helped me figure out the solution, but your answer as it stands doesn't answer the question because it's asserting inheritance. I want to give you the accepted answer here, though - can you edit your answer so I do this without being misleading? – Jeff Mar 24 '13 at 20:21