【问题标题】:Compare two List<string[]> objects in C# Unit Test比较 C# 单元测试中的两个 List<string[]> 对象
【发布时间】:2018-02-26 09:53:42
【问题描述】:

我正在尝试创建一个比较两个字符串数组列表的单元测试。

我尝试创建两个完全相同的List&lt;string[]&gt; 对象,但是当我使用CollectionAssert.AreEqual(expected, actual); 时,测试失败:

[TestMethod]
public void TestList()
{
    List<string[]> expected = new List<string[]> {
        new string[] { "John", "Smith", "200" },
        new string[] { "John", "Doe", "-100" }
    };

    List<string[]> actual = new List<string[]> {
        new string[] { "John", "Smith", "200" },
        new string[] { "John", "Doe", "-100" }
    };

    CollectionAssert.AreEqual(expected, actual);
}

我也试过Assert.IsTrue(expected.SequenceEqual(actual));,但也失败了。

如果我比较两个字符串列表或两个字符串数组,这两种方法都有效,但在比较两个字符串数组列表时它们不起作用。

我假设这些方法失败了,因为它们正在比较两个对象引用列表而不是数组字符串值。

如何比较两个List&lt;string[]&gt; 对象并判断它们是否真的相同?

【问题讨论】:

  • 试试这个:expected.Zip(actual, (e, a) =&gt; e.SequenceEqual(a)).All(x =&gt; x).
  • 出于好奇...如果列表具有相同的元素但顺序不同,您会认为它们“相等”吗?此外,值得注意的是,字符串数组是对象的糟糕替代品。
  • @David 对于这个特定的测试,我可以接受要求元素顺序相同的解决方案,以及忽略顺序的解决方案。我同意对象通常比字符串数组更好。此代码是大图的一部分,需要采用这种格式。
  • @JeppeStigNielsen - .Zip 没有比较完整的长度。
  • .Contains(false) negated 并不比 .All(x =&gt; x) 更有效(也不会更低)。两者都“使用”源,直到找到false 的条目。第一个将每个元素与falsebool 的默认相等比较器进行比较。第二个调用委托predicate,它包装了来自lambda箭头x =&gt; x的静态IL方法,并检查返回值。由于运行时在任何一种情况下都会进行内联,我认为两者都会同样快(我没有测量)。同意@Enigmativity。

标签: c# arrays list unit-testing equality


【解决方案1】:

它失败了,因为列表中的项目是对象 (string[]),并且由于您没有指定 CollectionAssert.AreEqual 应该如何比较两个序列中的元素,所以它回退到比较引用的默认行为.例如,如果您要将列表更改为以下内容,您会发现测试通过了,因为现在两个列表都引用了相同的数组:

var first = new string[] { "John", "Smith", "200" };
var second = new string[] { "John", "Smith", "200" };

List<string[]> expected = new List<string[]> { first, second};
List<string[]> actual = new List<string[]> { first, second};

为避免引用比较,您需要告诉 CollectionAssert.AreEqual 如何比较元素,您可以通过在调用时传入 IComparer 来做到这一点:

CollectionAssert.AreEqual(expected, actual, StructuralComparisons.StructuralComparer);

【讨论】:

    【解决方案2】:

    CollectionAssert.AreEqual(expected, actual); 失败,因为它比较对象引用。 expectedactual 指的是不同的对象。

    Assert.IsTrue(expected.SequenceEqual(actual)); 因同样的原因而失败。这次比较expectedactual的内容,但是元素本身是不同的数组引用。

    也许尝试使用SelectMany 来展平两个序列:

    var expectedSequence = expected.SelectMany(x => x).ToList();
    var actualSequence = actual.SelectMany(x => x).ToList();
    CollectionAssert.AreEqual(expectedSequence, actualSequence);
    

    正如 Enigmativity 在他的评论中正确指出的那样,当数组和/或其元素的数量不同时,SelectMany 可能会给出肯定的结果,但展平列表将导致元素数量相等。只有在数组和这些数组中的元素数量始终相同的情况下才是安全的。

    【讨论】:

    • 您已经通过对象引用的解释搞定了,但是.SelectMany 方法会产生一大堆“误报”,并且不需要.Select(y =&gt; y)
    • 感谢您的评论。我错过了。我编辑了我的帖子。
    【解决方案3】:

    最好的解决方案是检查每个子集合中的项目以及每个相应子集合中的项目数。

    试试这个:

    bool equals = expected.Count == actual.Count &&
                  Enumerable.Range(0, expected.Count).All(i => expected[i].Length == actual[i].Length &&
                                                               expected[i].SequenceEqual(actual[i]));
    Assert.IsTrue(equals);
    

    这将检查:

    • 两个列表的长度相同
    • 两个列表中的所有子集合对都具有相同的长度
    • 每对子集合中的项目都是一样的

    注意:使用SelectMany 不是一个好主意,因为它可能会造成误报,您的第二个列表中有相同的项目,但分布在不同的子集合中。我的意思是,即使第二个列表在一个子集合中都有相同的项目,它也会认为两个列表是相同的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多