简体   繁体   中英

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?

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.

The DeepEqual library makes it quite simple now. It is enough to write:

results.WithDeepEqual(expected).IgnoreProperty<MySubClass>(x => x.Id).Assert();

If we take Hayden unit tests and slightly supplement them, you get something like this:

using System;
using Xunit;
using DeepEqual.Syntax;
using System.Collections.Generic;

public class MyClass
{
    public List<MySubClass> MyList { get; } = new List<MySubClass>
    {
        new MySubClass {Id = Guid.NewGuid(), Name = "Foo"},
        new MySubClass {Id = Guid.NewGuid(), Name = "Bar"},
        new MySubClass {Id = Guid.NewGuid(), Name = "Test"}
    };
}

public class MySubClass
{
    public Guid Id { get; init; }

    public string Name { get; init; }
}

public class DeepEqualTests
{
    [Fact]
    public void FullDeepEqualTest()
    {
        // Arrange
        var first = new MyClass();
        var second = new MyClass();

        // Act
        Action compassion = () => first
            .ShouldDeepEqual(second);

        // Assert
        Assert.Throws<DeepEqualException>(compassion);
    }

    [Fact]
    public void DeepEqualWithoutIdTest()
    {
        // Arrange
        var first = new MyClass();
        var second = new MyClass();

        // Act
        Action compassion = () => first
            .WithDeepEqual(second)
            .IgnoreProperty<MySubClass>(x => x.Id)
            .Assert();

        // Assert
        compassion();
    }
}

The first test is a full comparison and expects an exception. The second test will ignore the ID field when comparing and succeed if no exception is thrown. Both tests run successfully

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM