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: }



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…
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.
Explaining GetHashCode method. « Vadim’s Weblog said
[...] 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 [...]