C# Assert.AreEqual如何确定两个通用IEnumerable之间的相等性?

C# Assert.AreEqual如何确定两个通用IEnumerable之间的相等性?,c#,unit-testing,ienumerable,mstest,assert,C#,Unit Testing,Ienumerable,Mstest,Assert,我有一个单元测试来检查一个方法是否返回正确的IEnumerable。该方法使用yield-return构建可枚举项。其可枚举的类如下所示: enum TokenType { NUMBER, COMMAND, ARITHMETIC, } internal class Token { public TokenType type { get; set; } public string text { get; set; } public static b

我有一个单元测试来检查一个方法是否返回正确的
IEnumerable
。该方法使用
yield-return
构建可枚举项。其可枚举的类如下所示:

enum TokenType
{
    NUMBER,
    COMMAND,
    ARITHMETIC,
}

internal class Token
{
    public TokenType type { get; set; }
    public string text { get; set; }
    public static bool operator == (Token lh, Token rh) { return (lh.type == rh.type) && (lh.text == rh.text); }
    public static bool operator != (Token lh, Token rh) { return !(lh == rh); }
    public override int GetHashCode()
    {
        return text.GetHashCode() % type.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        return this == (Token)obj;
    }
}
这是该方法的相关部分:

 foreach (var lookup in REGEX_MAPPING)
 {
     if (lookup.re.IsMatch(s))
     {
         yield return new Token { type = lookup.type, text = s };
         break;
     }
 }
如果我将此方法的结果存储在
actual
中,则创建另一个可枚举的
expected
,并像这样比较它们

  Assert.AreEqual(expected, actual);
,则断言失败

我为
IEnumerable
编写了一个扩展方法,类似于(它将两个IEnumerable组合成一组对),并尝试了以下方法:

foreach(Token[] t in expected.zip(actual))
{
    Assert.AreEqual(t[0], t[1]);
}

成功了!那么这两个
Assert.AreEqual
s之间的区别是什么呢?

Assert.AreEqual
将比较手头的两个对象
IEnumerable
s本身就是类型,并提供了一种机制来迭代某个集合……但它们实际上不是该集合。您最初的比较比较了两个
IEnumerable
s,这是一个有效的比较…但不是您需要的。您需要比较这两个
IEnumerable
s要枚举的内容

以下是我如何比较两个可枚举项:

Assert.AreEqual(t1.Count(), t2.Count());

IEnumerator<Token> e1 = t1.GetEnumerator();
IEnumerator<Token> e2 = t2.GetEnumerator();

while (e1.MoveNext() && e2.MoveNext())
{
    Assert.AreEqual(e1.Current, e2.Current);
}
Assert.AreEqual(t1.Count(),t2.Count());
IEnumerator e1=t1.GetEnumerator();
IEnumerator e2=t2.GetEnumerator();
而(e1.MoveNext()&&e2.MoveNext())
{
断言.AreEqual(e1.Current,e2.Current);
}
我不确定上面的代码是否比您的
.Zip
方法少,但它非常简单。

找到了它:

Assert.IsTrue(expected.SequenceEqual(actual));

您是否考虑过改用
CollectionAssert
类……考虑到它旨在对集合执行相等性检查

附录:

如果要比较的“集合”是枚举,那么简单地用“
新列表(枚举)
”包装它们是执行比较的最简单方法。构建新列表当然会带来一些开销,但是,在单元测试的环境中,我希望这不会太重要?

我认为最简单、最清晰的方法是将jerryjvl的答案和MEMark在其帖子上的评论结合起来,使用扩展方法组合
CollectionAssert.AreEqual

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
这提供了比OP建议的SequenceEqual答案更丰富的错误信息(它将告诉您找到了哪个元素是意外的)。例如:

IEnumerable<string> expected = new List<string> { "a", "b" };
IEnumerable<string> actual   = new List<string> { "a", "c" }; // mismatching second element

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
// Helpful failure message!
//  CollectionAssert.AreEqual failed. (Element at index 1 do not match.)    

Assert.IsTrue(expected.SequenceEqual(actual));
// Mediocre failure message:
//  Assert.IsTrue failed.   

@杰森·贝克:请不要误会,你有没有考虑过你不得不问这样的问题,这可能意味着你把事情弄得太复杂了?呃。。。不,不是真的。:-)你能告诉我在哪里把事情弄复杂吗?另外,我不相信使用“text.GetHashCode()%type.GetHashCode();”作为GetHashCode()的返回值是个好主意…@Mitch Wheat-我也不确定。。。但这是另一个问题的主题。这是有意义的。没有任何方法可以在不编写尽可能多的代码的情况下逐个比较这两个IEnumerables对象吗?我只会使用while循环。我用一个例子更新了我的答案。我找到了另一种更简单有效的方法。我会接受这一点,因为您已经说明了为什么我的代码不起作用。:-)一些提示:这种方法需要两次检查两个序列(一次用于计数,一次用于每个项目)——效率不高。另外,IEnumerator是可识别的,所以应该包括“使用”。最后,您可以使用EqualityComparer.Default.Equals(e1.Current,e2.Current)来避免装箱等操作。是的,它确实迭代了两次……但这是一个简单的实现。;)有一个更复杂的版本可以更有效地完成工作,但不够清晰。如果你比较两个非常大的枚举,需要一个更有效的方法,但我喜欢我的清晰。(然而,使用语句确实应该被使用……为了简洁起见,请忽略。)很好的发现!我甚至不知道有SequenceEqual()扩展。泰!非常感谢。您保存了我的循环;-)这不会告诉您测试失败时哪个元素不相等的任何信息;它只会告诉你它失败了。jerryjvl建议的CollectionAssert机制提供了更丰富的故障信息。请确保在using指令中包含System.Linq。尽管CollectionAssert中的所有方法都设置为与ICollections进行比较。我有数不清的人。有什么简单的方法可以将它们更改为ICollections吗?在这种情况下,我通常只需在
新列表(枚举)
中快速总结,以便执行比较。在单元测试的上下文中,开销可能不是问题。在使用CollectionAssert时,只需对两个参数执行.ToArray()。
CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList());
// really helpful error message!
//  CollectionAssert.AreEquivalent failed. The expected collection contains 1
//  occurrence(s) of <b>. The actual collection contains 0 occurrence(s).