views:

289

answers:

2

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<System.Collections.Generic.IDictionary<string,string>>' does not contain a definition for 'ToList' and no extension method 'ToList' accepting a first argument of type 'System.Collections.Generic.IEnumerator<System.Collections.Generic.IDictionary<string,string>>' could be found

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

Error 3
'System.Collections.Generic.IEnumerator<System.Collections.Generic.IDictionary<string,string>>' does not contain a definition for 'SequenceEquals' and no extension method 'SequenceEquals' accepting a first argument of type 'System.Collections.Generic.IEnumerator<System.Collections.Generic.IDictionary<string,string>>' 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?

A: 

You could easily write your own generic version, then move it to a base or utility class that's used in all of your tests. Base it on the LINQ operators like All and Any.

John Saunders
+6  A: 

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 with the LINQ extension methods, like this:

Assert.IsTrue(expected.SequenceEquals(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;
    }
}
Mark Seemann
This gives me compiler errors (see above).
Rosarch
You have to import the System.Linq namespace in a using directive. `ToList` is an extension method.
Mark Seemann
I added `using System.Linq;` but it still doesn't work: `'System.Collections.Generic.IEnumerator<System.Collections.Generic.IDictionary<string,string>>' does not contain a definition for 'ToList' and no extension method 'ToList' accepting a first argument of type 'System.Collections.Generic.IEnumerator<System.Collections.Generic.IDictionary<string,string>>' could be found`
Rosarch
`ToList` doesn't extend `IEnumerator<T>`, it extends `IEnumerable<T>`. Remove the call to `GetEnumerator()`.
Mark Seemann
Right right. Ok, but I still have the problem that this is comparing lists, but I don't care about the order of objects in the collection. I'm just working with sets.
Rosarch
That would be addressed by CollectionAssert.AreEquivalent, then.
Mark Seemann
That fails for me. (See above.)
Rosarch