CollectionAssert use with generics?

Nick Heiner picture Nick Heiner · Mar 14, 2010 · Viewed 12.8k times · Source

It appears that CollectionAssert cannot be used with generics. This is super frustrating; the code I want to test does use generics. What am I to do? Write boilerplate to convert between the two? Manually check collection equivalence?

This fails:

ICollection<IDictionary<string, string>> expected = // ...

IEnumerable<IDictionary<string, string>> actual = // ...

// error 1 and 2 here
CollectionAssert.AreEqual(expected.GetEnumerator().ToList(), actual.ToList());

// error 3 here
Assert.IsTrue(expected.GetEnumerator().SequenceEquals(actual));

Compiler errors:

Error 1:

'System.Collections.Generic.IEnumerator>' does not contain a definition for 'ToList' and no extension method 'ToList' accepting a first argument of type 'System.Collections.Generic.IEnumerator>' could be found

Error 2

'System.Collections.Generic.IEnumerator>' does not contain a definition for 'ToList' and no extension method 'ToList' accepting a first argument of type 'System.Collections.Generic.IEnumerator>' could be found

Error 3

'System.Collections.Generic.IEnumerator>' does not contain a definition for 'SequenceEquals' and no extension method 'SequenceEquals' accepting a first argument of type 'System.Collections.Generic.IEnumerator>' could be found

What am I doing wrong? Am I not using extensions correctly?

Update: Ok, this looks a bit better, but still doesn't work:

IEnumerable<IDictionary<string, string>> expected = // ...

IEnumerable<IDictionary<string, string>> actual = // ...

CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList()); // fails
CollectionAssert.IsSubsetOf(expected.ToList(), actual.ToList()); // fails

I don't want to be comparing lists; I only care about set membership equality. The order of the members is unimportant. How can I get around this?

Answer

Mark Seemann picture Mark Seemann · Mar 14, 2010

You can use CollectionAssert with generic collections. The trick is to understand that the CollectionAssert methods operate on ICollection, and although few generic collection interfaces implement ICollection, List<T> does.

Thus, you can get around this limitation by using the ToList extension method:

IEnumerable<Foo> expected = //...
IEnumerable<Foo> actual = //...
CollectionAssert.AreEqual(expected.ToList(), actual.ToList());

That said, I still consider CollectionAssert broken in a lot of other ways, so I tend to use Assert.IsTrue(bool) with the LINQ extension methods, like this:

Assert.IsTrue(expected.SequenceEqual(actual));

FWIW, I'm currently using these extension methods to perform other comparisons:

public static class EnumerableExtension
{
    public static bool IsEquivalentTo(this IEnumerable first, IEnumerable second)
    {
        var secondList = second.Cast<object>().ToList();
        foreach (var item in first)
        {
            var index = secondList.FindIndex(item.Equals);
            if (index < 0)
            {
                return false;
            }
            secondList.RemoveAt(index);
        }
        return secondList.Count == 0;
    }

    public static bool IsSubsetOf(this IEnumerable first, IEnumerable second)
    {
        var secondList = second.Cast<object>().ToList();
        foreach (var item in first)
        {
            var index = secondList.FindIndex(item.Equals);
            if (index < 0)
            {
                return false;
            }
            secondList.RemoveAt(index);
        }
        return true;
    }
}