views:

2223

answers:

5

I'm trying to assert that one object is "equal" to another object.

The objects are just instances of a class with a bunch of public properties. Is there an easy way to have NUnit assert equality based on the properties?

This is my current solution but I think there may be something better:

Assert.AreEqual(LeftObject.Property1, RightObject.Property1)
Assert.AreEqual(LeftObject.Property2, RightObject.Property2)
Assert.AreEqual(LeftObject.Property3, RightObject.Property3)
...
Assert.AreEqual(LeftObject.PropertyN, RightObject.PropertyN)

What I'm going for would be in the same spirit as the CollectionEquivalentConstraint wherein NUnit verifies that the contents of two collections are identical.

+5  A: 

Override .Equals for your object and in the unit test you can then simply do this:

Assert.AreEqual(LeftObject, RightObject);

Of course, this might mean you just move all the individual comparisons to the .Equals method, but it would allow you to reuse that implementation for multiple tests, and probably makes sense to have if objects should be able to compare themselves with siblings anyway.

Lasse V. Karlsen
Of course, you'd create tests for your Equals methods...
David Kemp
Thanks, lassevk. This worked for me! I implemented .Equals according to the guidelines here: http://msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Michael Haren
And GetHashCode(), obviously ;-p
Marc Gravell
Number 1 on the list on that page is to override GetHashCode, and he did say he followed those guidelines :) But yes, common mistake to ignore that. Typically not a mistake you'll notice most of the time, but when you do, it's like one of those times when you say "Oh, hey, why's this snake up my trousers and why is he biting my ass".
Lasse V. Karlsen
+14  A: 

If you can't override Equals for any reason, you can build a helper method that iterates through public properties by reflection and assert each property. Something like this:

public static class AssertEx
{

        public static void PropertyValuesAreEquals(object actual, object expected)
     {
      PropertyInfo[] properties = expected.GetType().GetProperties();
      foreach (PropertyInfo property in properties)
      {
       object expectedValue = property.GetValue(expected, null);
       object actualValue = property.GetValue(actual, null);

       if (actualValue is IList)
        AssertListsAreEquals(property, (IList)actualValue, (IList)expectedValue);
       else if (!Equals(expectedValue, actualValue))
        Assert.Fail("Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
      }
     }

        private static void AssertListsAreEquals(PropertyInfo property, IList actualList, IList expectedList)
        {
         if (actualList.Count != expectedList.Count)
          Assert.Fail("Property {0}.{1} does not match. Expected IList containing {2} elements but was IList containing {3} elements", property.PropertyType.Name, property.Name, expectedList.Count, actualList.Count);

         for (int i = 0; i < actualList.Count; i++)
          if (!Equals(actualList[i], expectedList[i]))
           Assert.Fail("Property {0}.{1} does not match. Expected IList with element {1} equals to {2} but was IList with element {1} equals to {3}", property.PropertyType.Name, property.Name, expectedList[i], actualList[i]);
        }
}
Juanma
Type.GetProperties() returns all of the properties of the type. Not just the public ones.
Wesley Wiser
+6  A: 

I prefer not to override Equals just to enable testing. Don't forget that if you do override Equals you really should override GetHashCode also or you may get unexpected results if you are using your objects in a dictionary for example.

I do like the reflection approach above as it caters for the addition of properties in the future.

For a quick and simple solution however its often easiest to either create a helper method that tests if the objects are equal, or implement IEqualityComparer on a class you keep private to your tests. When using IEqualityComparer solution you dont need to bother with the implementation of GetHashCode. For example:

// Sample class.  This would be in your main assembly.
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Unit tests
[TestFixture]
public class PersonTests
{
    private class PersonComparer : IEqualityComparer<Person>
    {
        public bool Equals(Person x, Person y)
        {
            return (x.Name == y.Name) && (x.Age == y.Age);
        }

        public int GetHashCode(Person obj)
        {
            throw new NotImplementedException();
        }
    }

    [Test]
    public void Test_PersonComparer()
    {
        Person p1 = new Person { Name = "Tom", Age = 20 }; // Control data

        Person p2 = new Person { Name = "Tom", Age = 20 }; // Same as control
        Person p3 = new Person { Name = "Tom", Age = 30 }; // Different age
        Person p4 = new Person { Name = "Bob", Age = 20 }; // Different name.

        Assert.IsTrue(new PersonComparer().Equals(p1, p2), "People have same values");
        Assert.IsFalse(new PersonComparer().Equals(p1, p3), "People have different ages.");
        Assert.IsFalse(new PersonComparer().Equals(p1, p4), "People have different names.");
    }
}
ChrisYoxall
A: 

I agree with ChrisYoxall -- implementing Equals in your main code purely for testing purposes is not good.

If you are implementing Equals because some application logic requires it, then that's fine, but keep pure testing-only code out of cluttering up stuff (also the semantics of checking the same for testing may be different than what your app requires).

In short, keep testing-only code out of your class.

Simple shallow comparison of properties using reflection should be enough for most classes, although you may need to recurse if your objects have complex properties. If following references, beware of circular references or similar.

Sly

Sly
Nice catch on circular references. Easy to overcome if you keep a dictionary of objects already in the comparison tree.
Lucas B
A: 

I implemented a reusable class that compares two object graphs using reflection. The special thing about this implementation is the flexible configuration.

Here is a sample usage:

ObjectComparer comparer = new ObjectComparer();

// ignore MyClass.Id
comparer.Configure<MyClass>()
  .Property("Id", x => x.Compare(false));

// compare MyStruct not by properties, but by calling Equals
comparer.Configure<MyStruct>()
  .CompareMethod(CompareMethod.Equals));

comparer.Compare(object1, object2);

It's hard to show what it is capable to do in a somewhat declarative way.

I would like to publish this together with similar stuff as OSS, but need some more time to refine and the agreement of the company I'm working for.

Stefan Steinegger
did you every get this published?
Ian Ringrose
@Ian: not yet. I'm working on it, since I see that there is a strong demand for something like this.
Stefan Steinegger