wysek
wysek

Reputation: 83

recursive assert on collection

I would like a test like this one:

[Test]
    public void TestCollectionAssert ()
    {
        var a1 = new [] { new [] { "a" } };
        var a2 = new [] { new [] { "a" } };

        Assert.AreNotEqual (a1, a2);
        //CollectionAssert.AreEqual (a1, a2);
        CollectionAssert.AreEquivalent (a1, a2);
    }

to pass. My real case is more complicated, but solving this one in a generic way will do. Any ideas?

Upvotes: 4

Views: 1480

Answers (4)

Sean McLeish
Sean McLeish

Reputation: 1

I implemented following method, to compare the elements recursively. It worked so far for all my test cases, I could miss an edge case.

    /// <summary>
    ///     Compares two enumerables recursively.
    /// </summary>
    /// <param name="left">IEnumerable</param>
    /// <param name="right">IEnumerable</param>
    /// <returns>bool</returns>
    public static bool SequenceEqual(this IEnumerable left, IEnumerable right)
    {
        if (ReferenceEquals(left, right))
        {
            return true;
        }

        if (ReferenceEquals(null, left))
        {
            return false;
        }


        if (left.GetType() != right.GetType())
        {
            return false;
        }

        var leftEnumerator = left.GetEnumerator();
        var rightEnumerator = right.GetEnumerator();

        while (leftEnumerator.MoveNext())
        {
            if (!rightEnumerator.MoveNext())
            {
                return false;
            }

            if (typeof(IEnumerable).IsAssignableFrom(leftEnumerator.Current.GetType()))
            {
                if (!typeof(IEnumerable).IsAssignableFrom(rightEnumerator.Current.GetType()))
                {
                    return false;
                }

                if (!SequenceEqual((IEnumerable)leftEnumerator.Current, (IEnumerable)rightEnumerator.Current))
                {
                    return false;
                }
            }
            else
            {
                if (!Equals(leftEnumerator.Current, rightEnumerator.Current))
                {
                    return false;
                }
            }
        }

        if (rightEnumerator.MoveNext())
        {
            return false;
        }

        return true;
    }

Upvotes: 0

Charlie
Charlie

Reputation: 13736

It's an old question, but somebody just posted a link on the nunit-discuss list, so I'll answer.

NUnit is designed to report the two lists as equal. That's just how it works. The Assert.NotEqual should in fact fail.

Upvotes: 0

LBushkin
LBushkin

Reputation: 131806

There's a useful LINQ operator called SequenceEqual() which compares two sequences for equality. SequenceEqual() walks through any two IEnumerable<> sequences and verifies that they have the same number of elements and that elements at the same index are equal (using the default equality comparer). However, since you have nested collections, you need to extend the concept of equality to apply to them as well. Fortunately, there's an overload that allows you supply your own IEqualityComparer<> object.

Since it's awkward to constantly have to define a class to provide equality semantics, I've written a generic extension that allows you to use a delegate instead. Let's look at the code:

public static class ComparerExt
{
    private class GenericComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> m_EqualityFunc;

        public GenericComparer( Func<T,T,bool> compareFunc )
        {
            m_EqualityFunc = compareFunc;
        }

        public bool Equals(T x, T y)
        {
            return m_EqualityFunc(x, y);
        }
    }

    // converts a delegate into an IComparer
    public static IEqualityComparer<T> AreEqual<T>( Func<T,T,bool> compareFunc )
    {
        compareFunc.ThrowIfNull("compareFunc");

        return new GenericComparer<T>(compareFunc);
    }
}

Now, we can compare two sequences easily enough:

Assert.IsTrue( 
   // check that outer sequences are equivalent...
   a1.SequenceEqual( a2,
                     // define equality as inner sequences being equal... 
                     ComparerExt.AreEqual( (a,b) => a.SequenceEqual(b) ) );

Upvotes: 3

MatthiasG
MatthiasG

Reputation: 4532

You could write an extension method like the one below.

public class AssertExtension
{
    public bool AreSimilar<T>(this CollectionAssert, IList<T> list1, IList<T> list2)
    {
        // ...
    }
}

The problem is of course how to compare this two lists. I would suggest to step through the elements of the one list and try to remove them from the other. If there is a problem while doing this or at the end there are objects left they are not similar.

Upvotes: 0

Related Questions