Vadim's Weblog

Never stop learning.

Comparing Reference Types in Unit Tests.

Posted by Vadim on July 3, 2008

If you’ve done some unit testing, you’re familiar with Assert.AreEqual method.  Have you try to compare two objects that have the same value(s) but the AreEqual method tells you that they are not equal?  For example let assume that we have a class Point:

   1: private class Point
   2: {
   3:   private int _x;
   4:   private int _y;
   5:  
   6:   public Point(int x, int y)
   7:   {
   8:     _x = x;
   9:     _y = y;
  10:   }
  11: }

And we test for the constructor:

   1: [Test]
   2: public void Point_constructor_test()
   3: {
   4:   Point point = new Point(2, 3);
   5:   Assert.AreEqual(point, new Point(2, 3));
   6: }

When I run this test using MbUnit, the assertion fails.  The reason it fails because Point is a reference type.     Actually what is getting compared is objects references (not objects values) and of course they have different references.

One way we could fix it is to make Point a Value Type by replacing class with struct.

   1: private struct Point
   2: {
   3:   private int _x;
   4:   private int _y;
   5:   
   6:   public Point(int x, int y)
   7:   {
   8:     _x = x;
   9:     _y = y;
  10:   }
  11: }

This definitely resolves the issue.

Another thing we could do is implement IEquatable<T> interface:

   1: private class Point : IEquatable<Point>
   2: {
   3:   private int _x;
   4:   private int _y;
   5:  
   6:   public Point(int x, int y)
   7:   {
   8:     _x = x;
   9:     _y = y;
  10:   }
  11:  
  12:   public bool Equals(Point other)
  13:   {
  14:     return (_x == other._x) && (_y == other._y);
  15:   }
  16: }

[Updated] However, MbUnit test still will fail because deep inside MbUnit’s AreEqual calls object.Equal(object).  In order for AreEqual to succeed in this case, it needs to call generic version of AreEqual.  Something like this:

   1: private static bool AreEqual<T>(T expected, T actual)
   2: {
   3:   if (expected is IEquatable<T> )
   4:     return ((IEquatable<T>)expected).Equals(actual);
   5:   return expected.Equals(actual);
   6: }

It means we need to modify our test little bit. Instead of AreEquals method we can use IsTrue one.

   1: [Test]
   2: public void Point_constructor_test()
   3: {
   4:   Point point = new Point(2, 3);
   5:   Assert.IsTrue(point.Equals(new Point(2, 3)));
   6: }

In software development the same problem can be solved many different ways.  I assume that most common solution is going to be comparing some public properties in the object instead of the whole object.

Let assume assume that our Point class has public properties X and Y.  Here’s an example how my test would look:

   1: [Test]
   2: public void Point_constructor_test()
   3: {
   4:   Point point1 = new Point(2, 3);
   5:   Point point2 = new Point(2, 3);
   6:   Assert.AreEqual(point1.X, point2.X);
   7:   Assert.AreEqual(point1.Y, point2.Y);
   8: }

kick it on DotNetKicks.com

About these ads

3 Responses to “Comparing Reference Types in Unit Tests.”

  1. Jeff Brown said

    Well, actually object.Equals(object, object) calls object.Equals(object) if both values are non-null and are not referentially identical.

    Courtesy of Reflector:
    public static bool Equals(object objA, object objB)
    {
    return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
    }

    So I’m not sure if your post is completely correct here…

  2. Vadim said

    Jeff,
    You are absolutely right. I actually reflect on object.Equals(object, object) myself. But I forgot about it very fast. Thanks for keeping me honest. I’ll update this post.

  3. […] best way to explain it is to show an example. In my previous post I was using class Point as an example.  I’m going to continue with this class.  So […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: