断言两种不同类型的枚举是等价的
本文关键字:枚举 断言 同类型 两种 | 更新日期: 2023-09-27 17:50:48
我有一个NUnit单元测试,我有两个不同类型的集合,我想断言它们是等价的。
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);
}
其中Assert.IsPrettySimilar
定义如下
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);
}
}
}
我的问题是,是否有一种更习惯的方法来使用NUnit中现有的方法来完成上述操作?我已经看过CollectionAssert
了,没有什么符合我想做的。
我对"等效"的描述是:
1)集合必须具有相同的大小
2)集合必须有相同的逻辑顺序
3)必须使用某种谓词来确定匹配项之间的等价性。
让我们考虑一下您要测试的内容。您不需要测试第一个序列中的对象是否与第二个序列中的对象相同。它们可能非常不同。所以,类似的这个词在这里是非常模糊的。你真正要测试的是,第一个序列的某个投影等于第二个序列的另一个投影。NUnit已经有这样的功能了:
CollectionAssert.AreEqual(bunchOfAs.Select(a => a.ToString()),
bunchOfBs.Select(b => b));
因此,您正在投影两个序列以获得特定的数据,然后您可以为这两个投影给出很好的名称,这将使您的测试对其他人具有可读性。这里有一些隐藏的业务逻辑,在代码中没有解释——您没有解释为什么要做这样的预测。好的投影结果名称可以解释你的意图。例句:
var expectedNames = employees.Select(u => u.Login);
var actualNames = customers.Select(c => c.Name);
CollectionAssert.AreEqual(expectedNames, actualNames);
对我来说这比
干净多了 Assert.IsPrettySimilar(employees, customers, (e, c) => u.Login == c.Name);
我知道您研究了CollectionAssert
,但是,我发现这样的策略在我自己的测试中非常有用。
重写对象上的ToString和Equals使测试通过。
[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);
}
}
我故意把GetABunchOfBs弄乱了顺序,但是测试仍然通过了。
看起来Sergey的答案是我正在寻找的(这是看看NUnit是否已经有一个设施来做我想要的)。然而,这是我最终的解决方案,它更接近我想要的实现。
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));
}
}
}
}