35

我有一个单元测试来检查方法是否返回正确的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,请创建另一个 enumerable expected,然后像这样比较它们......

  Assert.AreEqual(expected, actual);

...,断言失败。

IEnumerable我为此编写了一个类似于Pythonzip函数的扩展方法(它将两个 IEnumerables 组合成一组对)并尝试了这个:

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

有效!Assert.AreEqual那么这两个s有什么区别呢?

4

4 回答 4

89

找到了:

Assert.IsTrue(expected.SequenceEqual(actual));
于 2009-06-01T01:54:13.180 回答
48

您是否考虑过使用CollectionAssert该类...考虑到它旨在对集合执行相等性检查?

附录:
如果被比较的“集合”是枚举,那么简单地用“ new List<T>(enumeration)”包装它们是执行比较的最简单方法。构建一个新列表当然会导致一些开销,但是在单元测试的上下文中,我希望这不应该太重要吗?

于 2009-06-01T02:01:47.597 回答
25

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

这是我比较两个可枚举的方法:

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

我不确定上面的代码是否比你的.Zip方法少,但它很简单。

于 2009-06-01T01:41:11.897 回答
21

我认为断言你想要的平等的最简单和最清晰的方法是结合 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.   

如果/当你的测试失败时,你会高兴你这样做了——有时你甚至可以知道什么是错的,而不必打破调试器——嘿,你做的 TDD 是对的,所以你先写一个失败的测试,对?;-)

AreEquivalent如果您用于测试等效性(顺序无关紧要),错误消息会更有帮助:

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).   
于 2012-06-18T19:11:52.670 回答