views:

336

answers:

4

Hi,

I'd like to know whether there is a way to compare two objects in MBUnit so that the test is passed when the objects "look" the same, even if those are distinct instances?

For example:

[TestFixture]
class ComparisonTestFixture
{

    class foo
       {
           public string bar;
       }

    [Test]
    public void ComparisonTest()
    {

        foo foo1 = new foo()
           {
               bar = "baz"
           };

        foo foo2 = new foo()
            {
                bar = "baz"
            };


        //This assertion should be successful, but it isn't
        //*** Failures ***
        //Expected values to be equal.
        //Expected Value & Actual Value : {foo: bar = "zzz...."}
        //Remark : Both values look the same when formatted but they are distinct instances.
        Assert.AreEqual(foo1,foo2);
    }
}

Assert.AreEqual() does not work for this (test fails, see source code above). Since it remarks that "Both values look the same when formatted but they are distinct instances", I figure there must be some way to do this built into MbUnit already without serializing the objects to XML in my own code.

Do I have to write my own Assert extension method for this?

+1  A: 

I would suggest that you override the Equals method on your class to perform the comparison you want. This allows you to define value equality instead of reference equality. One caveat is that you also have to override GetHashCode if you override Equals to ensure that two object that are equal also returns the same hash code. Here is a very simple example;

public class Foo {

  public String Bar {
    get;
    set;
  }

  public String Baz {
    get;
    set;
  }

  public override Boolean Equals(Object other) {
    Foo otherFoo = other as Foo;
    return otherFoo != null
      && Bar.Equals(otherFoo.Bar)
      && Baz.Equals(otherFoo.Baz);
  }

  public override Int32 GetHashCode() {
    return Bar.GetHashCode() ^ Baz.GetHasCode();
  }

}

If you don't want to override Equals and you really just want to compare instances property by property you could use reflection:

public static Boolean AreEqual<T>(T a, T b) {
  foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
    if (!Object.Equals(propertyInfo.GetValue(a, null),
                       propertyInfo.GetValue(b, null)))
      return false;
  return true;
}
Martin Liversage
But that would not be generic. I'd have to do this for every new class I am testing.
Adrian Grigore
But apparently you classes uses value equality and not reference equality. To capture this fact you should really consider overriding `Equals`. This might not only benefit your tests but also your application code.
Martin Liversage
In my actual application I do not need to compare two instances of this object type. If I would override Equals, that would be only for unit testing. I like to keep overhead code for unit testing at a minimum.
Adrian Grigore
Thanks for the edit! I'll use the second option you suggested for implementing an IEqualityComparer as mausch suggested.
Adrian Grigore
+3  A: 

There is an overload of Assert.AreEqual() that takes an IEqualityComparer<T> as parameter and another that takes a EqualityComparison<T>

Otherwise you could use Assert.AreEqual(Assert.XmlSerialize(a), Assert.XmlSerialize(b))

Mauricio Scheffer
Yes, but where's the advantage? I'd still have to implement my own EqualityComparioson object. I don't see any advantage over overloading equals or adding an extension method to Assert.
Adrian Grigore
I added another option
Mauricio Scheffer
The second option is what I have been trying too. It works ok in most cases, but serialization fails for some more objects due to Ninject being used behind the scenes. That's what made me start this thread.
Adrian Grigore
+4  A: 

Yann also implemented a StructuralEqualityComparer that compares property values one by one given a set of lambdas for each property. Worth looking at.

More info here: http://www.gallio.org/api/html/T%5FMbUnit%5FFramework%5FStructuralEqualityComparer%5F1.htm

Jeff Brown
Yup. As Jeff says, the structural equality comparer is the right tool to be used in that case. Several assertions accepts an IEqualityComparer<T> object are parameter (AreEqual, AreElementsEqual, etc.) the comparer specifies to the assertion how to compare types that are not equatable explicitly.Assert.AreEqual(foo1, foo2, new StructuralEqualityComparer<Foo> { { x => x.bar } });
Yann Trevin
You can also find a simple usage sample here: http://interfacingreality.blogspot.com/2009/06/assertdistinct-in-mbunit-v3.html
Yann Trevin
Looks nice, but a bit overblown for what I am trying to achieve. I'll use an EqualityComparer with reflexction instead. Still, thanks for the hint!
Adrian Grigore
Mmmh.. I'm not sure to understand why it's overblown. The StructuralEqualityComparer is a built-in feature of MbUnit v3. It's actually a flexible IEqualityComparer you can easily customize at will. And in the propose scenario, it's only one single line to use (as shown at the end of the 1st comment)
Yann Trevin
+1  A: 

What I usually do is just implement the ToString() override - which is considered best a best practice to do anyways.

so in your case :

public override string ToString()
     {
      return string.Format("Class foo, bar={0}",bar);
     }

then your AreEqual(foo1,foo2) will actually report the correct results as it will just call the default ToString implementation

Yuval
This does work, but it has the same disadvantage as overriding Equals. It's not generic, but requires extra code for each new object type that should be compared.
Adrian Grigore