用 ScalaTest 比较集合内容

Posted

技术标签:

【中文标题】用 ScalaTest 比较集合内容【英文标题】:Comparing collection contents with ScalaTest 【发布时间】:2011-11-18 02:01:34 【问题描述】:

我正在尝试对一些收集量很大的 Scala 进行单元测试。这些集合返回为Iterable[T],因此我对集合的内容 感兴趣,即使底层类型不同。这其实是两个相关的问题:

    如何断言两个 有序 集合包含相同的元素序列? 如何断言两个 无序 集合包含相同的元素集?

总之,我在 ScalaTest 中寻找 NUnit 的 CollectionAssert.AreEqual(有序)和 CollectionAssert.AreEquivalent(无序)的 Scala 等效项:

Set(1, 2) should equal (List(1, 2))          // ordered, pass
Iterable(2, 1) should equal (Iterable(1, 2)) // unordered, pass

【问题讨论】:

对于无序的情况,如果您不担心额外的内存使用,您可以在要比较的集合上调用.toSet 调用toSet 正是我想要的行为,但如果集合包含重复元素,它的行为就会不正确。 你可以使用groupBy方法,并传入一些身份函数作为映射器。 【参考方案1】:

同时你可以使用

Iterable(2, 1) should contain theSameElementsAs Iterable(1, 2)

要测试有序集,您必须将其转换为序列。

Set(1, 2).toSeq should contain theSameElementsInOrderAs List(1, 2)

【讨论】:

回复:第二个例子。 Set 没有定义的顺序,因此转换为 Seq 时的顺序本质上是随机的,您永远不应该有一个结果取决于该顺序的测试。如果您使用像SortedSettoSeq 这样的有序集合,那么should contain theSameElementsInOrderAsshould equal 有何不同,除了一个更吸引人的名称? 有没有办法让sbt 提供比.... did not contain the same elements as ... 更好的输出?具体来说,如果它能告诉我大型收藏中缺少什么,那就太好了。 如何递归使用?将案例类与集合成员进行比较是很常见的。【参考方案2】:

您可以尝试.toSeq 用于有序集合,.toSet 用于无序集合,据我所知,它捕获了您想要的内容。

以下通行证:

class Temp extends FunSuite with ShouldMatchers 
  test("1")   Array(1, 2).toSeq should equal (List(1, 2).toSeq) 
  test("2")   Array(2, 1).toSeq should not equal (List(1, 2).toSeq) 
  test("2b")  Array(2, 1) should not equal (List(1, 2))   
  test("3")   Iterable(2, 1).toSet should equal (Iterable(1, 2).toSet) 
  test("4")   Iterable(2, 1) should not equal (Iterable(1, 2)) 

顺便说一句,Set 没有被订购。

编辑:为避免删除重复元素,请尝试toSeq.sorted。以下通行证:

  test("5")   Iterable(2, 1).toSeq.sorted should equal (Iterable(1, 2).toSeq.sorted) 
  test("6")   Iterable(2, 1).toSeq should not equal (Iterable(1, 2).toSeq) 

编辑2:对于元素无法排序的无序集合,可以使用这种方法:

  def sameAs[A](c: Traversable[A], d: Traversable[A]): Boolean = 
    if (c.isEmpty) d.isEmpty
    else 
      val (e, f) = d span (c.head !=)
      if (f.isEmpty) false else sameAs(c.tail, e ++ f.tail)
    

例如(注意使用符号'a 'b 'c 没有定义的顺序)

  test("7")   assert( sameAs(Iterable(2, 1),    Iterable(1, 2)     )) 
  test("8")   assert( sameAs(Array('a, 'c, 'b), List('c, 'a, 'b)   )) 
  test("9")   assert( sameAs("cba",             Set('a', 'b', 'c') )) 

替代sameAs实现:

  def sameAs[A](c: Traversable[A], d: Traversable[A]) = 
    def counts(e: Traversable[A]) = e groupBy identity mapValues (_.size)
    counts(c) == counts(d)
  

【讨论】:

感谢您的回答。这与我正在寻找的非常接近,但不完全是:toSet 消除了重复值,因此Array(1, 1).toSet should not equal (Array(1)) 错误地失败了。据我所知,Scala 没有 MultiSet 集合。你有什么建议吗? @Michael 好点,看我的编辑。如果您的商品未订购(即您无法对它们进行排序),则可能会有点棘手 定义排序并不理想,但在这种情况下是可行的。谢谢! 这个答案已经过时了。 ScalaTest API 已得到改进。 正如@ChristophDittberner 已经说过的,ScalaTest 3.0 提供了Matchers to work with containers。

以上是关于用 ScalaTest 比较集合内容的主要内容,如果未能解决你的问题,请参考以下文章

Sbt 无法解析 Intellij IDEA 上的 Scalatest

使用 ScalaTest 时从 SBT 中排除特别标记的测试

如何查找内容并使用Scala sbt安装它们?

多级测试类层次结构中的 Spring @ContextHierarchy(使用 Scala 和 ScalaTest)

Scalatest 问题:价值 FunSuite 不是 org.scalatest 的成员

scalatest : 对象 scalatest 不是包 org 的成员