Assert.AreEqual 如何确定两个泛型 IEnumerable 之间的相等性?

发布于 2024-07-21 14:13:45 字数 1441 浏览 7 评论 0原文

我有一个单元测试来检查方法是否返回正确的 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 编写了一个扩展方法,类似于 Python 的 zip 函数(它将两个 IEnumerables 组合成一组对)并尝试了这个:

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

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

I have a unit test to check whether a method returns the correct IEnumerable. The method builds the enumerable using yield return. The class that it is an enumerable of is below:

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

This is the relevant part of the method:

 foreach (var lookup in REGEX_MAPPING)
 {
     if (lookup.re.IsMatch(s))
     {
         yield return new Token { type = lookup.type, text = s };
         break;
     }
 }

If I store the result of this method in actual, make another enumerable expected, and compare them like this...

  Assert.AreEqual(expected, actual);

..., the assertion fails.

I wrote an extension method for IEnumerable that is similar to Python's zip function (it combines two IEnumerables into a set of pairs) and tried this:

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

It worked! So what is the difference between these two Assert.AreEquals?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

嘿哥们儿 2024-07-28 14:13:45

找到了:

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

Found it:

Assert.IsTrue(expected.SequenceEqual(actual));
薯片软お妹 2024-07-28 14:13:45

您是否考虑过使用CollectionAssert类来代替......考虑到它的目的是对集合执行相等检查?

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

Have you considered using the CollectionAssert class instead...considering that it is intended to perform equality checks on collections?

Addendum:
If the 'collections' being compared are enumerations, then simply wrapping them with 'new List<T>(enumeration)' is the easiest way to perform the comparison. Constructing a new list causes some overhead of course, but in the context of a unit test this should not matter too much I hope?

酒废 2024-07-28 14:13:45

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

以下是我比较两个枚举的方法:

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 方法少,但它非常简单。

Assert.AreEqual is going to compare the two objects at hand. IEnumerables are types in and of themselves, and provide a mechanism to iterate over some collection...but they are not actually that collection. Your original comparison compared two IEnumerables, which is a valid comparison...but not what you needed. You needed to compare what the two IEnumerables were intended to enumerate.

Here is how I compare two enumerables:

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

I am not sure whether the above is less code than your .Zip method, but it is about as simple as it gets.

黑色毁心梦 2024-07-28 14:13:45

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

I think the simplest and clearest way to assert the equality you want is a combination of the answer by jerryjvl and comment on his post by MEMark - combine CollectionAssert.AreEqual with extension methods:

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());

This gives richer error information than the SequenceEqual answer suggested by the OP (it will tell you which element was found that was unexpected). For example:

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.   

You'll be really pleased you did it this way if/when your test fails - sometimes you can even know what's wrong without having to break out the debugger - and hey you're doing TDD right, so you write a failing test first, right? ;-)

The error messages get even more helpful if you're using AreEquivalent to test for equivalence (order doesn't matter):

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).   
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文