7

I am currently trying to learn how to use unit testing, and I have created the actual list of 3 animal objects and the expected list of 3 animal objects. The question is how do I Assert to check the lists are equal? I have tried CollectionAssert.AreEqual and Assert.AreEqual but to no avail. Any help would be appreciated.

The test method:

  [TestMethod]
    public void createAnimalsTest2()
    {
        animalHandler animalHandler = new animalHandler();
        // arrange
        List<Animal> expected = new List<Animal>();
        Animal dog = new Dog("",0);
        Animal cat = new Cat("",0);
        Animal mouse = new Mouse("",0);
        expected.Add(dog);
        expected.Add(cat);
        expected.Add(mouse);
        //actual
        List<Animal> actual = animalHandler.createAnimals("","","",0,0,0);


        //assert
        //this is the line that does not evaluate as true
        Assert.Equals(expected ,actual);

    }
DIF
  • 2,470
  • 6
  • 35
  • 49
JamesZeinzu
  • 235
  • 2
  • 3
  • 12
  • Have a look at the answer to this S.O. post: [http://stackoverflow.com/questions/5194966/mstest-collectionassert-areequivalent-failed-the-expected-collection-contains][1] [1]: http://stackoverflow.com/questions/5194966/mstest-collectionassert-areequivalent-failed-the-expected-collection-contains – Andrew Oct 24 '13 at 10:23
  • This worked, but I cant make your comment the answer, thank you for the help, I tried searching for the answer but couldnt find it. – JamesZeinzu Oct 24 '13 at 10:29

4 Answers4

14

That is correct, as the lists might look the same, they are 2 different objects containing the same data.

In order to compare lists, you should use the CollectionAssert

CollectionAssert.AreEqual(expected, actual);

That should do the trick.

pazcal
  • 929
  • 5
  • 26
  • 4
    Hmm, CollectionAssert.AreEqual doesnt work, it says: CollectionAssert.AreEqual failed. (Element at index 0 do not match.) – JamesZeinzu Oct 24 '13 at 10:11
  • I understand this might be because even though the objects hold the same info, they are not the same object. Is there a different method that will compare the objects content instead of the object itself? – JamesZeinzu Oct 24 '13 at 10:15
  • 2
    It should work once you use a custom IComparer along with CollectionAssert.AreEqual. Should be the third parameter. See http://msdn.microsoft.com/de-de/library/vstudio/ms243703.aspx – Jesko R. Oct 24 '13 at 16:00
7

Just incase someone comes across this in the future, the answer was I had to create an Override, IEqualityComparer as described below:

public class MyPersonEqualityComparer : IEqualityComparer<MyPerson>
{
public bool Equals(MyPerson x, MyPerson y)
{
    if (object.ReferenceEquals(x, y)) return true;

    if (object.ReferenceEquals(x, null)||object.ReferenceEquals(y, null)) return false;

    return x.Name == y.Name && x.Age == y.Age;
}

public int GetHashCode(MyPerson obj)
{
    if (object.ReferenceEquals(obj, null)) return 0;

    int hashCodeName = obj.Name == null ? 0 : obj.Name.GetHashCode();
    int hasCodeAge = obj.Age.GetHashCode();

    return hashCodeName ^ hasCodeAge;
}

}

JamesZeinzu
  • 235
  • 2
  • 3
  • 12
1

I am of the opinion that implementing the IEqualityComparer (Equals() and GetHashCode()) for only testing purpose is a code smell. I would rather use the following assertion method, where you can freely define that on which properties you want to do the assertions:

public static void AssertListEquals<TE, TA>(Action<TE, TA> asserter, IEnumerable<TE> expected, IEnumerable<TA> actual)
{
    IList<TA> actualList = actual.ToList();
    IList<TE> expectedList = expected.ToList();

    Assert.True(
        actualList.Count == expectedList.Count,
        $"Lists have different sizes. Expected list: {expectedList.Count}, actual list: {actualList.Count}");

    for (var i = 0; i < expectedList.Count; i++)
    {
        try
        {
            asserter.Invoke(expectedList[i], actualList[i]);
        }
        catch (Exception e)
        {
            Assert.True(false, $"Assertion failed because: {e.Message}");
        }
    }
}

In action it would look like as follows:

    public void TestMethod()
    {
        //Arrange
        //...

        //Act
        //...

        //Assert
        AssertAnimals(expectedAnimals, actualAnimals);
    }

    private void AssertAnimals(IEnumerable<Animal> expectedAnimals, IEnumerable<Animal> actualAnimals)
    {
        ListAsserter.AssertListEquals(
            (e,a) => AssertAnimal(e,a),
            expectedAnimals,
            actualAnimals);
    }

    private void AssertAnimal(Animal expected, Animal actual)
    {
        Assert.Equal(expected.Name, actual.Name);
        Assert.Equal(expected.Weight, actual.Weight);
        //Additional properties to assert...
    }

I am using XUnit for the simple Assert.True(...) and Assert.Equals(), but you can use any other unit test library for that. Hope it helps someone! ;)

mirind4
  • 1,423
  • 4
  • 21
  • 29
0

I modified method AssertListEquals() and used standard Assert.All()

    public static void AssertListEquals<TE, TA>(IEnumerable<TE> expected, IEnumerable<TA> actual, Action<TE, TA> asserter)
    {
        if (expected == null && actual == null) return;
        Assert.NotNull(expected);
        Assert.NotNull(actual);

        Assert.True(
            actual.Count() == expected.Count(),
            $"Lists have different sizes. Expected list: {expected.Count()}, actual list: {actual.Count()}");

        var i = 0;
        Assert.All(expected, e =>
        {
            try
            {
                asserter(e, actual.Skip(i).First());
            }
            finally
            {
                i++;
            }
        });
    }