Question

I'm creating the unit test that will need to compare two objects of the same type memberwise. I've decided to use SemanticComparison library to handle this task without writing custom comparer code. It works really well when comparing flat objects, there are problems when the object contains nested object that also need to be compared memberwise.

public class Outer
{
    public string Name { get; set; }
    public Inner Inner { get; set; }
}

public class Inner
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class Service
{
    public Outer Method()
    {
        return new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value1"
            }
        };
    }
}

This won't work because Inner object is compared by reference, not memberwise:

    [Test]
    public void SimpleTest1()
    {

        // setup
        var expectedLikeness = new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value1"
            }
        }.AsSource().OfLikeness<Outer>();

        var sut = new Service();
        // exercise sut
        var actual = sut.Method();
        // verify
        expectedLikeness.ShouldEqual(actual);
    }

To make it work I had to create proxy of the nested object so that it overrides the default equals implementation.

    [Test]
    public void SimpleTest2()
    {

        // setup
        var expectedLikeness = new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value1"
            }.AsSource().OfLikeness<Inner>().CreateProxy()
        }.AsSource().OfLikeness<Outer>();

        var sut = new Service();
        // exercise sut
        var actual = sut.Method();
        // verify
        expectedLikeness.ShouldEqual(actual);
    }

Well, it works properly, but imagine that after some service code refactoring we introduce the bug that causes the value property of the Inner class be different from the expected value. The cool feature of the SemanticComparison is that it can log the name of the member that causes inequality. But, in this case, it'll only return "Inner" as mismatch, not the name of the specific property in the Inner class.

Am I missing something? Is it possible to configure it to be able to return actual mismatch member.

This is obviously not an issue for simple data structures as in this example, but it could be an inconvenience for testing a real life code.

Was it helpful?

Solution

Since nobody answered, I'll provide my own answer.

So, it seems that you cannot do it OOTB, unless you write some extra code. I've wrapped the code in set of extension methods. These methods let you specify what inner properties/collection properties should be compared using inner likeness, not by reference. You don't need to create any proxies manually, everything is handled internally by these extensions. And the results of all internal comparisons are logged, so you can see exactly what member has invalid value.

Here is the test from the question with usage of "WithInnerLikeness" extension method.

    [Test]
    public void ServiceTest3()
    {
        // setup
        var expected = new Outer()
        {
            Name = "outerName",
            Inner = new Inner()
            {
                Name = "innerName",
                Value = "value2"
            }
        };

        var expectedLikeness = expected.AsSource().OfLikeness<Outer>()
            .WithInnerLikeness(d => d.Inner, s => s.Inner)
            ;

        var sut = new Service();
        // exercise sut
        var actual = sut.Method();
        // verify
        expectedLikeness.ShouldEqual(actual);
    }

You can see the value properties of inner objects do not match, so the test should fail. And it fails with the following messages in the output:

Comparing inner properties using likeness. Source: s => s.Inner Destination: d => d.Inner.
The source and destination values are not equal. Details: The provided value ClassLibrary1.Inner did not match the expected value ClassLibrary1.Inner. The following members did not match:
- Value.

Ploeh.SemanticComparison.LikenessException : The provided value ClassLibrary1.Outer did not match the expected value ClassLibrary1.Outer. The following members did not match:
- Inner.

You can find the source code and more examples on github.

https://github.com/jmansar/SemanticComparisonExtensions

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top