Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assert two different types of enumerations are equivalent

I have an NUnit unit test which I have two collections of different types which I want to assert are equivalent.

class A { public int x; }
class B { public string y; }

[Test]
public void MyUnitTest()
{
    var a = GetABunchOfAs(); // returns IEnumerable<A>
    var b = GetABunchOfBs(); // returns IEnumerable<B>

    Assert.IsPrettySimilar(a, b, (argA, argB) => argA.ToString() == argB);
}

where Assert.IsPrettySimilar is defined like such

public static void IsPrettySimilar<T1, T2>(
    IEnumerable<T1> left, 
    IEnumerable<T2> right, 
    Func<T1, T2, bool> predicate)
{
    using (var leftEnumerator = left.GetEnumerator())
    using (var rightEnumerator = right.GetEnumerator())
    {       
        while (true)
        {
            var leftMoved = leftEnumerator.MoveNext();

            if (leftMoved != rightEnumerator.MoveNext()) 
            {
                Assert.Fail("Enumerators not of equal size");
            }

            if (!leftMoved)
            {
                break;
            }

            var isMatch = predicate(leftEnumerator.Current, 
                                rightEnumerator.Current);

            Assert.IsTrue(isMatch);
        }           
    }
}

My question is, is there a more idiomatic way of doing the above with the existing methods in NUnit? I already looked at CollectionAssert and there's nothing matching what I want to do.

My description of "equivalent" in this case is:

1) Collections must be of same size
2) Collections must be in same logical order
3) Some predicate must be used to determine equivalence between matching items.

like image 341
Matthew Avatar asked Mar 17 '14 15:03

Matthew


2 Answers

Let's think what you are trying to test. You are not trying to test that objects from first sequence are same as objects from second sequence. They can be very different. So, word similar is very vague here. What you really trying to test here, is that some projection of first sequence is equal to other projection of second sequence. And NUnit already have such functionality:

 CollectionAssert.AreEqual(bunchOfAs.Select(a => a.ToString()),
                           bunchOfBs.Select(b => b));

Thus you are projecting both sequences to get particular data, then you can give nice names for these two projections, which will make your test readable to others. You have some hidden business logic here, which does not have explanation in code - you don't explain why you making such projections. So, nice names of projection results will explain your intent. E.g:

 var expectedNames = employees.Select(u => u.Login);
 var actualNames = customers.Select(c => c.Name);
 CollectionAssert.AreEqual(expectedNames, actualNames);

That is much cleaner for me than

 Assert.IsPrettySimilar(employees, customers, (e, c) => u.Login == c.Name);
like image 171
Sergey Berezovskiy Avatar answered Sep 19 '22 17:09

Sergey Berezovskiy


I know you looked into CollectionAssert, however, I have found a strategy like this very useful in my own tests.

Overriding ToString and Equals on the objects makes this test pass.

[TestFixture]
public class Class1
{

    [Test]
    public void MyUnitTest()
    {
        var a = GetABunchOfAs(); // returns IEnumerable<A>
        var b = GetABunchOfBs(); // returns IEnumerable<B>

        CollectionAssert.AreEqual(a, b.OrderBy(x => x.y));

    }

    public List<A> GetABunchOfAs()
    {
        return new List<A>
        {
            new A() {x = 1},
            new A() {x = 2},
            new A() {x = 3},
            new A() {x = 4}
        };
    }

    public List<B> GetABunchOfBs()
    {
        return new List<B>
        {
            new B() {y = "4"},
            new B() {y = "1"},
            new B() {y = "2"},
            new B() {y = "3"},

        };
    }
}

public class A
{
    public int x;
    public override bool Equals(object obj)
    {
        return obj.ToString().Equals(x.ToString());
    }

    public override string ToString()
    {
        return x.ToString();
    }
}

public class B
{
    public string y;

    public override string ToString()
    {
        return y;
    }

    public override bool Equals(object obj)
    {
        return obj.ToString().Equals(y);
    }

}

I deliberately left GetABunchOfBs out of order, however the test still passes.

like image 44
Tim Sneed Avatar answered Sep 22 '22 17:09

Tim Sneed