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
Expand NUnitEquality to use Equality of all Properties #4394
Comments
@manfred-brands Just to confirm, this would only be for two objects of the same type? Supporting cross-type "shape" checks had some complicated edge cases in #4244 |
@stevenaw This would only be when both actual and expected have the same type. |
I could see this being helpful, and could've used such a feature within the last few weeks. How did you envision the API looking and acting? |
Wait, perhaps we're thinking different things. Were you thinking this is a new constraint + API, or a change to the existing ones? EDIT: Could you give a code sample of how you saw this working? |
Either an built-in comparer that a user can pass in if the user knows the type at hand doesn't implement Assert.That(model, Is.EqualTo(_Model).Using(PropertyComparer.Instance)); Or do that automatically by NUnit for all classes that don't implement |
Here an implementation that seem to work for simple cases. using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace NUnit.Framework.Constraints.Comparers
{
internal sealed class PropertyComparer : IEqualityComparer
{
public static PropertyComparer Instance { get; } = new PropertyComparer();
bool IEqualityComparer.Equals(object? x, object? y) => PropertyEquals(x, y);
int IEqualityComparer.GetHashCode([DisallowNull] object obj) => throw new NotImplementedException();
private static bool PropertyEquals(object? x, object? y)
{
if (ReferenceEquals(x, y))
return true;
if (x is null || y is null)
return false;
Type type = x.GetType();
if (type != y.GetType())
return false;
// Shortcut if the type at hand implements IEquatable<T>
MethodInfo? equalsMethod = type.GetMethod(
"Equals",
BindingFlags.Instance | BindingFlags.ExactBinding | BindingFlags.Public,
null,
new[] { type },
null);
if (equalsMethod is not null)
{
return (bool)equalsMethod.Invoke(x, new[] { y })!;
}
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
object? xPropertyValue = property.GetValue(x, null);
object? yPropertyValue = property.GetValue(y, null);
if (!PropertyEquals(xPropertyValue, yPropertyValue))
{
return false;
}
}
return true;
}
}
} |
@manfred-brands sorry it's taken me a bit to respond, I've been wanting to get to a computer to test a few things but that's proven difficult. I like this idea! Particularly that it would minimize the amount of "test-only" code one may need to write, but also that it's opt-in. I'd been wanting a computer to experiment on some potential usages with how this may also be exposed as a property of a constraint itself (like how we use |
We have several class that we don't want to implement
IEquatable<>
for the sake of NUnit tests.To compare these we have created a
ReflectionAssert.PropertiesEqual
class.This class gets all the properties of a class and calls the method recursively on those.
If any properties implements
IEquatable<>
it will call theEquals
method instead.It would be a nice feature to add for NUnit4 so that we can compare more classes.
The text was updated successfully, but these errors were encountered: