CollectionAssert use with generics?

13,475

Solution 1

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;
    }
}

Solution 2

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));

Solution 3

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.

Share:
13,475
Nick Heiner
Author by

Nick Heiner

JS enthusiast by day, horse mask enthusiast by night. Talks I've Done

Updated on June 25, 2022

Comments

  • Nick Heiner
    Nick Heiner almost 2 years

    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?

  • Mark Seemann
    Mark Seemann about 14 years
    You have to import the System.Linq namespace in a using directive. ToList is an extension method.
  • Nick Heiner
    Nick Heiner about 14 years
    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.G‌​eneric.IDictionary<s‌​tring,string>>' could be found
  • Mark Seemann
    Mark Seemann about 14 years
    ToList doesn't extend IEnumerator<T>, it extends IEnumerable<T>. Remove the call to GetEnumerator().
  • Nick Heiner
    Nick Heiner about 14 years
    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.
  • Mark Seemann
    Mark Seemann about 14 years
    That would be addressed by CollectionAssert.AreEquivalent, then.
  • Nick Heiner
    Nick Heiner about 14 years
    That fails for me. (See above.)
  • an phu
    an phu almost 9 years
    SequenceEquals is misspelled; should be SequenceEqual (without the 's')