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

时间:2014-03-17 15:42:31

标签: c# .net unit-testing nunit

我有一个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)必须使用一些谓词来确定匹配项之间的等价性。

3 个答案:

答案 0 :(得分:1)

我知道你调查了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排除在外,但测试仍然通过。

答案 1 :(得分:1)

让我们考虑一下您要测试的内容。您没有尝试测试第一个序列中的对象与第二个序列中的对象相同。它们可能非常不同。所以,类似这个词在这里很模糊。你真正想在这里测试的是,第一序列的某些投影等于第二序列的其他投影。 NUnit已经拥有了这样的功能:

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

因此,您要投射两个序列以获取特定数据,然后您可以为这两个投影指定漂亮的名称,这将使您的测试对其他人可读。你在这里有一些隐藏的业务逻辑,在代码中没有解释 - 你不能解释为什么你做出这样的预测。因此,投影结果的好名称将解释您的意图。 E.g:

 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);

答案 2 :(得分:0)

看起来谢尔盖的答案是我正在寻找的答案(这是为了看看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));
            }
        }
    }
}