Unit Testing Generic Lists

If you do unit tests frequently, know that unit testing a generic list is a very common scenario because generic lists are a common part of today's codes in the .NET world.

Testing the equality of two generic lists isn't as easy as easy testing the equality of two objects from two simple types.  In order to test two generic lists, you need to follow one of two approaches that I describe in this post.  One of them is easy and can be done quickly and the second one may need more effort.

Background

First suppose that you have a Person class like this:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace GenericList

{

    public class Person

    {

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public int Age { get; set; }

        public string Sex { get; set; }

 

        public Person()

        {

        }

 

        public Person(string firstName, string lastName, int age, string sex)

        {

            this.FirstName = firstName;

            this.LastName = lastName;

            this.Age = age;

            this.Sex = sex;

        }

    }

}

And also suppose that you have another class named PersonCollection derived from generic list of Person.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace GenericList

{

    public class PersonCollection : List<Person>

    {

        public Person GetOldest()

        {

            int maxAge = 0;

            Person chosenPerson = new Person();

 

            foreach (Person person in this)

            {

                if (person.Age > maxAge)

                {

                    maxAge = person.Age;

                    chosenPerson = person;

                }

            }

 

            return chosenPerson;

        }

    }

}

Now I write a People class that doesn't have anything but a List property of PersonCollection type.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace GenericList

{

    public class People

    {

        public PersonCollection List { get; set; }

    }

}

Override Equals Method

The first approach, and the approach that I personally would prefer to the second one, is overriding the Equals method for the generic list class in your code.  In this case, when you call Assert.AreEqual to check the equality of two lists of this type, then it can compare two lists in the right way that you teach to it.

In the above example, I can update the PersonCollection class to override the Equals method.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace GenericList

{

    public class PersonCollection : List<Person>

    {

        public Person GetOldest()

        {

            int maxAge = 0;

            Person chosenPerson = new Person();

 

            foreach (Person person in this)

            {

                if (person.Age > maxAge)

                {

                    maxAge = person.Age;

                    chosenPerson = person;

                }

            }

 

            return chosenPerson;

        }

 

        public override bool Equals(object obj)

        {

            PersonCollection input = obj as PersonCollection;

 

            if (input.Count != this.Count)

                return false;

 

            for (int index = 0; index < input.Count; index++)

            {

                Person inputPerson = input[index];

                Person originalPerson = this[index];

 

                if ((inputPerson.FirstName != originalPerson.FirstName) ||

                    (inputPerson.LastName != originalPerson.LastName) ||

                    (inputPerson.Age != originalPerson.Age) ||

                    (inputPerson.Sex != originalPerson.Sex))

                    return false;

            }

            return true;

        }

 

        public override int GetHashCode()

        {

            return base.GetHashCode();

        }

    }

}

Now I can write a test method to test the List property of People class.

/// <summary>

///A test for List

///</summary>

[TestMethod()]

public void ListTest()

{

    People target = new People();

    PersonCollection expected = new PersonCollection();

    expected.Add(new Person("Keyvan", "Nayyeri", 23, "Male"));

    expected.Add(new Person("Mehrdad", "Ebrahimi", 24, "Male"));

 

    PersonCollection actual = new PersonCollection();

    target.List = expected;

    actual = target.List;

    Assert.AreEqual(expected, actual);

}

For two reasons this may be hard or impossible for you:

Despite these two negative points, this approach is better because:

Write a Generic Test Method

The second approach is easier to achieve but works as just a testing solution and can't provide abovementioned benefits for you.

You can simply write a generic test method that gets two generic lists and tests them on fly.

/// <summary>

///A test for List

///</summary>

[TestMethod()]

public void ListTest()

{

    People target = new People();

    PersonCollection expected = new PersonCollection();

    expected.Add(new Person("Keyvan", "Nayyeri", 23, "Male"));

    expected.Add(new Person("Mehrdad", "Ebrahimi", 24, "Male"));

 

    PersonCollection actual = new PersonCollection();

    target.List = expected;

    actual = target.List;

 

    GenericTest<Person>(expected, actual);

}

 

private void GenericTest<T>(List<T> actual, List<T> expected)

{

    IEnumerator<T> actualEnumerator = actual.GetEnumerator();

 

    for (int i = 0; i < expected.Count; i++)

    {

        Assert.IsTrue(actualEnumerator.MoveNext());

        Assert.IsTrue(actualEnumerator.Current.Equals(expected[i]));

    }

 

    Assert.IsFalse(actualEnumerator.MoveNext());

}

This is a quick way and I use it when want to test codes faster!

Test Results

[advertisement] Axosoft OnTime 2008 is four developer tools in one: bug tracking, project wiki, feature management, and help desk. It manages your development process so developers can focus on coding. Installed or Hosted – Free Single-user license -- Free 30-day team trial.

2 Comments : 09.30.07

Feedbacks

 avatar
#1
DotNetKicks.com
09.30.2007 @ 7:01 PM
You've been kicked (a good thing) - Trackback from DotNetKicks.com
 avatar
#2
Greg Young
11.22.2007 @ 6:52 PM
if ((inputPerson.FirstName != originalPerson.FirstName) || (inputPerson.LastName != originalPerson.LastName) || (inputPerson.Age != originalPerson.Age) || (inputPerson.Sex != originalPerson.Sex)) return false; This smells like a problem ... shouldn't I being asking the person if it is the same as the other person? What happens when I add Person.SocialSecurityNumber? The unit tests will still pass if two different people with 2 different social security numbers ... In general I would either override .Equals in Person and in this code say if Person.Equals(OtherPErson) or I would pass a Comparer into the collection so that I as an observer could determine if the two people were the same... Nice write up.. Cheers, Greg

Leave a Comment