1

I'm creating unit tests in which I will be comparing lists of objects with one another.

Currently I am using Fluent assertions in combination with specflow and nunit. I already use the Fluent Assertions to make a comparison as following:

public void TestShizzle()
{
    // I normally retrieve these lists from a moq database or a specflow table
    var expected = list<myObject> 
    {
        new myObject 
        {
            A = 1,
            B = "abc"
        }
    }

    var found = list<myObject> 
    {
        new myObject 
        {
            A = 1,
            B = "def"
        }
    }

    // this comparison only compares a few columns. The comparison is also object dependent. I would like to make this dynamic
    found.Should().BeEquivalentTo(
        expected,
        options =>
        options.Including(x => x.A));
}

What I really want is to be able to use generics instead of a specified type. I also want to decide which properties to compare at compile time. This is because of the large number of tables in the database. I think i need to use Linq Expressions for this, but I don't know how to go about this. The function should look something like this:

public void GenericShizzle<T>(List<T> expected, List<T> found, IEnumerable<PropertyInfo> properties)
{
    Expression<Func<T, object>> principal;
    foreach(var property in properties)
    {
        // create the expression for including fields
    }

    found.Should().BeEquivalentTo(
        expected,
        options =>
        // here is need to apply the expression.
}

I have no real idea how to get the correct expression for the job, or if this even the best method. I think I need to create an property expression that is understood by the include function, but maybe a different method can be used?

martijn
  • 1,417
  • 1
  • 16
  • 26
  • As I understand, you want to be able to compare two objects, without knowing their class, is that correct? And the comparison will always be performed between two objects of the same type, is that correct too? – Dylan El Bar Apr 26 '22 at 12:52
  • yes, I want to compare two lists of data of the same type, but only a set of class properties need to be compared and not all of them. – martijn Apr 26 '22 at 12:58
  • And how are you planning to get this property list? Why not just pass corresponding expression to `GenericShizzle`? – Guru Stron Apr 26 '22 at 13:03
  • I am using specflow. With this test suite I can define a table object with row values and column names. I will reverse engineer the class properties from this table – martijn Apr 26 '22 at 13:06

1 Answers1

2

There is Including method overload accepting Expression<Func<IMemberInfo, bool>> which can be used to dynamically filter members based on information about them:

IEnumerable<PropertyInfo> properties = ...;
var names = properties
    .Select(info => info.Name)
    .ToHashSet();
found.Should()
    .BeEquivalentTo(expected,
         options => options.Including((IMemberInfo mi) => names.Contains(mi.Name))); // or just .Including(mi => names.Contains(mi.Name))
Guru Stron
  • 102,774
  • 10
  • 95
  • 132