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.
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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With