Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value equality on subset of object properties #4687

Open
Rabadash8820 opened this issue Apr 11, 2024 · 3 comments
Open

Value equality on subset of object properties #4687

Rabadash8820 opened this issue Apr 11, 2024 · 3 comments
Labels

Comments

@Rabadash8820
Copy link

Rabadash8820 commented Apr 11, 2024

It would be pretty handy if there was a way to do value equality on a subset of an object's properties. For example, suppose I have the following class definition:

class Thing {
    public int SomeInt { get; set; }
    public double SomeDouble { get; set; }
    public string SomeString { get; set; }
}

and a method that returns a Thing. Let's say that the method sets non-default values for all the object's properties, but I only care about the values of the return object's numeric properties (SomeInt and SomeDouble). My options are to:

  1. Do individual equality assertions on those properties, which doesn't scale very cleanly for real-world objects with many properties.

    Assert.That(thing.SomeInt, Is.EqualTo(expectedInt));
    Assert.That(thing.SomeDouble, Is.EqualTo(expectedDouble));
  2. Define an "expected" object and use equality assertion with UsingPropertiesComparer, which requires me to include those other non-default values in my expected object, even though my test really only cares about the two numeric properties (also doesn't scale well for objects with many properties).

    Assert.That(thing, Is.EqualTo(new Thing {
        SomeInt = 5,
        SomeDouble = 5d,
        SomeString = "bla bla don't care",
    }).UsingPropertiesComparer());

It would be great if NUnit had a version of UsingPropertiesComparer that only compared a subset of Thing's properties. Perhaps using an Expression or collection of Expressions for consumers to specify the properties that they care about. Something like the following would be awesome, as the test code would not have to change as more properties are added to Thing (obviously with better naming 😅).

Assert.That(thing, Is.EqualTo(new Thing {
    SomeInt = 5,
    SomeDouble = 5d,
}).UsingPropertiesComparerButOnlyOnThePropertiesSpecifiedAbove());
@stevenaw
Copy link
Member

stevenaw commented Apr 14, 2024

This is a neat idea, so I'm going to label it idea for the team to consider.
One thought about option 2 here is that if the expected is the same POCO type and not all properties are specified then it may be tough for the framework to understand if a prop not listed, and thus with a default value, should be considered or not. One alternative option which could be considered is passing an anonymous object instead.

Perhaps some variation involving:

Assert.That(thing, Is.EqualTo(new {
    SomeInt = 5,
    SomeDouble = 5d,
    SomeString = "bla bla don't care",
}).UsingPropertiesComparer());

@Rabadash8820
Copy link
Author

Ooh that's a cool idea. I assumed some Expression magic would be necessary for NUnit to distinguish whether a property was provided or not, but simply iterating the provided properties on an anonymous (or dynamic) object could be simpler. FWIW, I got this idea from ts-mockito (a TypeScript mocking library inspired by Java's Mockito library), where such "sub-equality" comparisons are possible. This may be due to the "duck typing" that TS uses under the hood, so I wasn't sure if this would be possible in .NET, but hopefully it will be with one of the above approaches.

@mikkelbu
Copy link
Member

@Rabadash8820 Usually, when I have to do assertions like this, then I use an assertion framework - e.g. FluentAssertions. Using FluentAssertions can specify it like this

orderDto.Should().BeEquivalentTo(order, options => options
    .Including(o => o.OrderNumber)
    .Including(pi => pi.Name == "Date"));

Or perhaps using ExcludingMissingMembers to only compare the properties that exists in both objects that are being compared

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants