Search code examples
c#deepequals

Ignore property with list type with DeepEquals


I am using the DeepEqual library, to test if the results of a test match my expected output.

I do the comparison simply as

results.ShouldDeepEqual(expected);

However, I don't know how to ignore a property within my list type

The type I am deep comparing contains a list. The data type held within this list contains a Guid property Id which I want to ignore, as well as a date.

Ignoring top level properties works fine. However, I don't see how to ignore properties on a list type.

To get around this for now, I have had to write some code to clear these properties myself, but this is obviously not ideal.

for (var i = 0; i < results.MyList.Count; i++)
{
    results.MyList[i].Id = Guid.Empty;
    expectedResults.MyList[i].Id = Guid.Empty;
}

How can I accomplish this?


Solution

  • Having a look through the source code of DeepEqual, the library allows for custom comparisons via the IComparison interface. With that, I simulated the models that you require:

    public class MyClass
    {
        public MyClass()
        {
            MyList = new List<MySubClass>(new[]
            {
                new MySubClass {Id = Guid.Parse("1".PadLeft(32, '0')), Name = "Foo"}, 
                new MySubClass {Id = Guid.Parse("2".PadLeft(32, '0')), Name = "Bar"}, 
                new MySubClass {Id = Guid.Parse("3".PadLeft(32, '0')), Name = "Test"}, 
            });
        }
    
        public MyClass(params MySubClass[] subs)
        {
            MyList = new List<MySubClass>(subs);
        }
        
        public List<MySubClass> MyList { get; set; }
    }
    
    public class MySubClass
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
    

    Then I went on to creating a custom IComparison class:

    public class MySubClassComparer : IComparison
    {
        public bool CanCompare(Type type1, Type type2)
        {
            return type1 == type2 && type1 == typeof(MySubClass);
        }
    
        public (ComparisonResult result, IComparisonContext context) Compare(IComparisonContext context, object value1, object value2)
        {
            if (value1 is MySubClass first && value2 is MySubClass second)
            {
                if (first.Name == second.Name)
                {
                    return (ComparisonResult.Pass, context);
                }
            }
    
            return (ComparisonResult.Fail, context);
        }
    }
    

    Here are both of my passing unit tests:

    [Fact]
    public void IsDeepEqual_ShouldReturnTrue_WhenListHasSameNames()
    {
        // Arrange
        MyClass sut = new MyClass();
        MyClass parameter = new MyClass(new[]
        {
            new MySubClass {Id = Guid.Parse("4".PadLeft(32, '0')), Name = "Foo"},
            new MySubClass {Id = Guid.Parse("5".PadLeft(32, '0')), Name = "Bar"},
            new MySubClass {Id = Guid.Parse("6".PadLeft(32, '0')), Name = "Test"},
        });
        var comparer = new ComparisonBuilder().WithCustomComparison(new MySubClassComparer()).Create();
    
        // Act
        bool result = sut.IsDeepEqual(parameter, comparer);
    
        // Assert
        Assert.True(result);
    }
    
    [Fact]
    public void IsDeepEqual_ShouldReturnFalse_WhenListHasDifferentNames()
    {
        // Arrange
        MyClass sut = new MyClass();
        MyClass parameter = new MyClass(new[]
        {
            new MySubClass {Id = Guid.Parse("4".PadLeft(32, '0')), Name = "Foo"},
            new MySubClass {Id = Guid.Parse("5".PadLeft(32, '0')), Name = "Bar"},
            new MySubClass {Id = Guid.Parse("6".PadLeft(32, '0')), Name = "Fail"},
        });
        var comparer = new ComparisonBuilder().WithCustomComparison(new MySubClassComparer()).Create();
    
        // Act
        bool result = sut.IsDeepEqual(parameter, comparer);
    
        // Assert
        Assert.False(result);
    }
    

    Notes This comparison should ignore the Id property, but am unsure if this is the best way to accomplish the task. There's an IgnoreProperty method which might be better suited for the task, but couldn't see a way to get it to work currently.

    If anyone has more experience then I do with the library, please let me know of better ways, and I'll update my answer accordingly.