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

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

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

总之,我在 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方法,并传入一些身份函数作为映射器。

标签: unit-testing scala scalatest


【解决方案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
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-04
  • 2018-11-20
相关资源
最近更新 更多