Geeks With Blogs
Chris Falter .NET Design and Best Practices

One of the cool features of Visual Studio 2005 is that it can generate a unit test stub for a class or method.  However, I recently discovered that the Unit Test plug-in does not know how to auto-generate an assertion for a method whose output includes a multi-dimensioned out parameter or return value.  Here's the relevant stub code that VS 2005 generated for one of my methods whose output includes a multi-dimensioned array:

// generated code that creates a class instance called "target", plus other input + expected output parameters

string[][] homeAttributes;
string[][] homeAttributes_expected = null; // TODO: Initialize to an appropriate value

target.MethodBeingTested(in1, in2, out homeAttributes)

CollectionAssert.AreEqual(homeAttributes_expected[i], homeAttributes[i], "homeAttributes was not set correctly.");

So I edited the code to initialize homeAttributes_expected, then ran the unit test, and it failed.  Okay, my code must have a bug...but when I reviewed the code for MethodBeingTested(), it sure looked right.  Then I stepped through the unit test in debug mode.  However, when I hit the breakpoint on CollectionAssert.AreEqual() and expanded the arrays, the data was exactly what I expected it to be.  So maybe my initialization code for homeAttributes_expected was wrong...but no, it looked right.  What was wrong with this picture?

(These are the kind of challenges that either bring out the best or the worst in a software developer.  You either rise to the occasion, or become a broken, miserable, and somewhat sneaky creature that silently excises the CollectionAssert.AreEqual() statement so the unit test will pass.  And I was not about to steal away quietly!)

I realized what must be happening: CollectionAssert.AreEqual() must be calling the object.Equals() method to compare each member of the collections.  And in a multi-dimensioned array, the collection object being tested (the array of arrays) consists of members that are themselves collections (i.e., arrays).  Since the built-in Array class in the .NET Framework does not override the object.Equals() method, CollectionAssert.AreEqual() will use the default object.Equals() method to compare the identity of the array members; in other words, it compares the address of the 2 arrays in memory.  These will of course be different, so the assertion fails, and the unit test fails.

You have probably noticed that this post claims to apply to all multi-dimensioned output.  That's because you will encounter the same problem if your output is a List<string>[], or a List<List<string>>, or any other multi-dimensioned object.

Fortunately, you can conquer this problem with some clever refactoring.  Just iterate through the outer dimension, and call CollectionAssert.AreEqual on the inner collections.  Write your code like this, and everything will work just the way you want it to:

for (int i = 0; i < homeAttributes_expected.GetLength(0); i++)
{
  CollectionAssert.AreEqual(homeAttributes_expected[i], homeAttributes[i], "MethodBeingTested_homeAttributes_expected was not set correctly at index " + i.ToString());
}

Now CollectionAssert.AreEqual() compares two single-dimensioned collections each time it is called.  As a result, when it calls object.Equals() on the members of the collections, it will use the member type's override of object.Equals()--in this case, String.Equals()--and the assertion works correctly.

Happy unit testing!

Posted on Friday, May 4, 2007 8:42 AM .NET Gotchas , Coding Practices and Design Patterns , Testing & Debugging | Back to top


Comments on this post: How To: Write a Unit Test for Multi-Dimensioned Output

# re: How To: Write a Unit Test for Multi-Dimensioned Output
Requesting Gravatar...
How can you test homeAttributes[i] when homeAttributes is multidimensional?
Left by craig on Aug 21, 2008 7:46 AM

Your comment:
 (will show your gravatar)


Copyright © Chris Falter | Powered by: GeeksWithBlogs.net