The statement of two different types of enumerations is equivalent

I have a NUnit unit test, which I have two collections of different types that I want to claim 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 as 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 to do this using existing methods in NUnit? I already looked at CollectionAssert and I don’t agree with what I want.

My description is "equivalent" in this case:

1) Collections should be the same size
2) Collections should be in the same logical order
3) To determine the equivalence of matching elements, you should use some predicate.

+6
source share
3 answers

Think about what you are trying to verify. You are not trying to verify that the objects from the first sequence are the same as the objects from the second sequence. They may be different. So a word like that is very vague. What you are really trying to check here is that some projection of the first sequence is equal to another projection of the second sequence. And NUnit already has this functionality:

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

Thus, you project both sequences to obtain specific data, then you can give good names for these two forecasts, which will make your test available to others. Here you have a hidden business logic that does not contain explanations in the code - you do not explain why you make such forecasts. Thus, the beautiful names of the projection results will explain your intentions. For instance:

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

This is much cleaner for me than

  Assert.IsPrettySimilar(employees, customers, (e, c) => u.Login == c.Name); 
+1
source

I know that you looked at CollectionAssert , however I found a strategy that is very useful in my own tests.

Overriding ToString and Equals on objects skips this test.

 [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 => xy)); } 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 intentionally left GetABunchOfBs out of order, however the test still passes.

+1
source

It seems that Sergey's answer is the one I'm looking for (to see if NUnit has the ability to do what I want). However, this is the solution I came across that is closer to the implementation I want.

 public static class EnumerableAssert { public static void AreEquivilent<TExpected, TActual>(IEnumerable<TExpected> expected, IEnumerable<TActual> actual, Func<TExpected, TActual, bool> predicate) { if (ReferenceEquals(expected, actual)) { return; } using (var expectedEnumerator = expected.GetEnumerator()) using (var actualEnumerator = actual.GetEnumerator()) { while (true) { var expectedMoved = expectedEnumerator.MoveNext(); if (expectedMoved != actualEnumerator.MoveNext()) { Assert.Fail("Expected and Actual collections are of different size"); } if (!expectedMoved) { return; } Assert.IsTrue(predicate(expectedEnumerator.Current, actualEnumerator.Current)); } } } } 
0
source

All Articles