autofixturesemantic-comparison

Why doesn't Autofixture Likeness behave like I'd expect for one of these two tests?


Given these classes:

public class DrumAndBassBand
{
    public Drums Drum { get; set; }
    public Bass Bass { get; set; }
}

public class Instrument
{
    public string Name { get; set; }
    public int SerialNumber { get; set; }
}
public class Drums : Instrument { }
public class Bass : Instrument { }

Why does this test pass...

[Fact]
public void DrumAndBassBand_Equality_Behaves_As_Expected_Version_One()
{
    // arrange
    var template = new Fixture().Create<DrumAndBassBand>();

    // act
    var createdBand = new DrumAndBassBand {Drum = template.Drum, Bass = template.Bass};

    // assert         
    var createdLikeness = createdBand.AsSource().OfLikeness<DrumAndBassBand>()
        .Without(x => x.Bass)
        .CreateProxy();
    createdLikeness.Drum = createdBand.Drum;

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

    var templateLikeness = template.AsSource().OfLikeness<DrumAndBassBand>()
        .Without(x => x.Bass)
        .CreateProxy();
    templateLikeness.Drum = template.Drum;

    Assert.True(templateLikeness.Equals(createdBand));
}

...and this one fail? (the difference is the DrumAndBaseBand instantiation)

[Fact]
public void DrumAndBassBand_Equality_Behaves_As_Expected_Version_Two()
{
    // arrange
    var template = new Fixture().Create<DrumAndBassBand>();

    // act
    var createdBand =
        new DrumAndBassBand
        {
            Drum = new Drums { Name = template.Drum.Name, SerialNumber = template.Drum.SerialNumber },
            Bass = new Bass { Name = template.Bass.Name, SerialNumber = template.Bass.SerialNumber }
        };

    // assert
    var createdLikeness = createdBand.AsSource().OfLikeness<DrumAndBassBand>()
        .Without(x => x.Bass)
        .CreateProxy();
    createdLikeness.Drum = createdBand.Drum;

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

    var templateLikeness = template.AsSource().OfLikeness<DrumAndBassBand>()
        .Without(x => x.Bass)
        .CreateProxy();
    templateLikeness.Drum = template.Drum;

    Assert.True(templateLikeness.Equals(createdBand));
}

Solution

  • In the second test, the Drum and Bass instances are different from the template where you are trying to compare.

    You can always run a Likeness (without creating a Proxy) and inspect the output:

    Test 'DrumAndBassBand_Equality_Behaves_As_Expected_Version_Two' failed: 
    
    Ploeh.SemanticComparison.LikenessException:
        The provided value DrumAndBassBand did not match the expected value DrumAndBassBand. 
    
    The following members did not match: 
    - Drum.
    

    That basically means that you have to provide a hint when creating a Likeness for the the comparison of the Drum instance.

    The first half of the test becomes:

    var createdLikeness = createdBand
        .AsSource().OfLikeness<DrumAndBassBand>()
        .With(x => x.Drum)
            .EqualsWhen((s, d) => s.Drum == createdBand.Drum)
        .Without(x => x.Bass)
        .CreateProxy();
    

    The source destination's Drums equals other Drums when it is really a createdBand instance Drum.

    Similarly, the second half of the test becomes:

    var templateLikeness = template
        .AsSource().OfLikeness<DrumAndBassBand>()
        .With(x => x.Drum)
            .EqualsWhen((s, d) => s.Drum == template.Drum)
        .Without(x => x.Bass)
        .CreateProxy();
    

    The above allow you to have very flexible comparisons (and you can always customize it even further).