36

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?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Nick Heiner
  • 119,074
  • 188
  • 476
  • 699
  • Are you sure? It's been giving me compiler errors, which I will post tomorrow morning. – Nick Heiner Mar 14 '10 at 05:19
  • I was wrong; CollectionAssert will NOT work on a ICollection. It works with ICollection only. This has been requested on Connect, but so far not implemented. Possible work around: http://stackoverflow.com/questions/662458/unittesting-ilist-with-collectionassert – Mitch Wheat Mar 14 '10 at 05:28
  • If I recall correctly, the ICollection and ICollection interfaces are VASTLY different. This is not the case of IEnumerable versus IEnumerable. Please check their methods. ICollection is uses for something OTHER than ICollection. ICollection doesn't even have ADD method - it is used for "more low-level" purposes like multithreading and marshalling. Therefore, I think you should rather find an interface that will suit your needs better, simple IEnumerable maybe? – quetzalcoatl Mar 09 '12 at 11:12
  • "The order of the members is unimportant" - that's exactly what `CollectionAssert.AreEquivalent` is for. What is the failure message you get with the test? (maybe your expected and actual are not equivalent!) – bacar Jun 18 '12 at 18:51
  • 1
    It's SequenceEqual, not SequenceEquals, that's probably your compilation error – Diego C. Jul 09 '12 at 18:48
  • 3 should work, this is what I use _Assert.IsTrue(expected.SequenceEqual(actual));_ – Ande Mar 17 '15 at 11:15
  • Have Microsoft open-sourced this library yet? Can we report and fix it so `CollectionAssert.AreEqual` works with more types? – Colonel Panic May 15 '15 at 15:42

3 Answers3

40

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;
    }
}
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    You have to import the System.Linq namespace in a using directive. `ToList` is an extension method. – Mark Seemann Mar 14 '10 at 17:40
  • I added `using System.Linq;` but it still doesn't work: `'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` – Nick Heiner Mar 14 '10 at 17:55
  • `ToList` doesn't extend `IEnumerator`, it extends `IEnumerable`. Remove the call to `GetEnumerator()`. – Mark Seemann Mar 14 '10 at 18:47
  • 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. – Nick Heiner Mar 14 '10 at 19:52
  • That would be addressed by CollectionAssert.AreEquivalent, then. – Mark Seemann Mar 14 '10 at 20:06
  • That fails for me. (See above.) – Nick Heiner Mar 14 '10 at 20:13
  • SequenceEquals is misspelled; should be SequenceEqual (without the 's') – an phu Jul 13 '15 at 19:39
6

If you are working with Sets, then use this Idiom

HashSet<string> set1  = new HashSet<string>(){"A","B"};
HashSet<string> set2  = new HashSet<string>(){"B","A"};

Assert.IsTrue(set1.SetEquals(set2));
Paweł Dyda
  • 18,366
  • 7
  • 57
  • 79
Sudipta
  • 61
  • 1
  • 1
1

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
  • 160,644
  • 26
  • 247
  • 397