I'm trying to unit-test some Scala that is very collection-heavy. These collections are returned as Iterable[T]
, so I am interested in the contents of the collection, even if the underlying types differ. This is actually two related problems:
- How do I assert that two ordered collections contain the same sequence of elements?
- How do I assert that two unordered collections contain the same set of elements?
In summary, I'm looking the Scala-equivalent of NUnit's CollectionAssert.AreEqual
(ordered) and CollectionAssert.AreEquivalent
(unordered) in ScalaTest:
Set(1, 2) should equal (List(1, 2)) // ordered, pass
Iterable(2, 1) should equal (Iterable(1, 2)) // unordered, pass
You could try .toSeq
for ordered collections and .toSet
for unordered, which captures what you want as far as I understand it.
The following passes:
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)) }
}
BTW a Set
is not ordered.
edit: To avoid removing duplicate elements, try toSeq.sorted
. The following pass:
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) }
edit 2: For unordered collections where elements cannot be sorted, you can use this method:
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)
}
e.g. (note use of symbols 'a 'b 'c
which have no defined ordering)
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') )) }
Alternative sameAs
implementation:
def sameAs[A](c: Traversable[A], d: Traversable[A]) = {
def counts(e: Traversable[A]) = e groupBy identity mapValues (_.size)
counts(c) == counts(d)
}
Meanwhile you can use
Iterable(2, 1) should contain theSameElementsAs Iterable(1, 2)
To test the ordered set you have to convert it to a sequence.
Set(1, 2).toSeq should contain theSameElementsInOrderAs List(1, 2)