断言两种不同类型的枚举是等价的

本文关键字:枚举 断言 同类型 两种 | 更新日期: 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));
            }
        }
    }
}