
I'm attempting to retrofit some test-driven-development concepts to a project I'm working on. One of the first things I discovered was I had neglected to consider the concept of equality in my data classes.
I use classes with nested collections to move data around, but I hadn't written any code to test the collections for equality. After some head-scratching and playing-around, I decided to use the IEquatable interface in the base type so I could pass a strongly-typed object to a .Equals method. Here's an example:
class Monkey: IEquatable
{
public string Name { get; set; }
public int Bananas { get; set; }
// IEquatable implementation
public bool Equals(Monkey other)
{
return (other.Name == Name && other.Bananas == Bananas);
}
}
If I had used the IComparable, I would have to test the type of the object before I test it for equality. This is where the new features of C#, even since 2.0, really stand out and make things easier. If you're still working on the 1.1 version of the framework, your life sucks.
Let's make some monkeys and some lists.
var Bonzo = new Monkey {Name = "Bonzo", Bananas = 7};
var Bobo = new Monkey {Name = "Bobo", Bananas = 16};
// make some groups
var group1 = new List { Bonzo, Bobo };
var group2 = new List { Bonzo, Bobo }; // matches group1
In reality, you may not have to worry about duplicate list members when you're working with a database. Or you may choose to use a hashtable that prevents duplicate entries.
Here's how I decided to test for set value equality: First of all, if the numbers of elements in the two sets don't match, there's no way they have equal value. Once that's out of the way, we just test to make sure all of the elements of the first set are in the second set. If the sets pass both tests, they are equal, as far as my app is concerned.
There are actually three ways to determine that group1 matches group2. Here they are in ascending order of coolness.
Console.WriteLine(group1.Count == group2.Count && group1.TrueForAll(m => group2.Contains(m)));
This method leverages the extension method for generic lists that tests if a given predicate is true for all members of the set. I used a lambda expression that will test all members of group1, m, are present in group2.
After I wrote that statement, ReSharper woke up and suggested I change it to this:
Console.WriteLine(group1.Count == group2.Count && group1.TrueForAll(group2.Contains));
ReSharper called this "convert to
method group". I guess since the elements of both groups are monkeys, and Contains takes a single monkey as an argument, it's obvious that the name of the method is all we need here?
The last way I tested this was at the suggestion of my cow-orker, Jeff. He suggested importing the LINQ library and stepping up from IList
to IEnumberable with the use of the All extension.Console.WriteLine(group1.Count == group2.Count && group1.All(group2.Contains));
Since IList implements IEnumerable, this steps the code up the evolutionary tree and may make it a little more bulletproof.
Now that I have the equality problem licked, I can get to writing some unit tests. I should have done this on the first day!
0 comments:
Post a Comment