12

I was looking for a ScalaTest matcher to check that a list contains all of the needed elements (given within another list), but that may also be others.

contain allOf demands to get two fixed elements, for some reason, the rest as varargs.

I can do a workaround like this, but it's tremendously ugly:

val list = List(1,2,3,4)
val wanted = List(1,2,3)
list should contain allOf ( wanted.head, wanted.tail.head, wanted.tail.tail  :_* )    // ugly workaround

For giving a list as the match, there is contain theSameElementsAs. However, it does not allow extraneous elements to be in the probed value (I think).

So:

  • am I missing something?
  • why is allOf declared in the way that it must be given two fixed elements in the front (i.e. why not just pass varargs?)
  • should there be theSameElementsAndMaybeMoreThanmethod (presumably with a better name)?

Some code I tried with:

val list = List.empty[String]
//list should contain allOf("a")         // does not compile
list should contain allOf("a","b")
list should contain allOf("a","b","c")

val wanted = List("a","b","c")
//list should contain allOf( wanted )    // does not compile
list should contain allOf( wanted.head, wanted.tail )   // compiles, but tests the wrong thing; against List(head,List(tail))

documentation:

Scala 2.11.4, ScalaTest 2.2.1

Edit:

I probably end up using something like:

wanted.foreach( list should contain(_) )

However, this does not seem as readable to me (the should is kind of embedded) as the built-in collection constructs.

akauppi
  • 17,018
  • 15
  • 95
  • 120
  • 3
    Have you considered using Sets? ```Set(1, 4).subsetOf(Set(1, 2, 3, 4, 5))``` – Anas Alkhatib Nov 07 '14 at 15:33
  • It kind of makes sense because it's not designed to work with lists but with items. Why it's 2 arguments and a vararg after that? Probably because if you want to give it less than 2, it won't be `allOf` anymore, it will be just `contain`. – Nader Ghanbari Nov 07 '14 at 17:39
  • @AnasAlkhatib No, I didn't consider sets, but conceptually that's exactly what I'm trying to do. Thanks for the hint! – akauppi Nov 08 '14 at 17:33
  • @NaderHadjiGhanbari Yes, that's similar to how I figured as to why the minimum was 2 params. It makes sense. I wasn't sure (still am not) whether it's really an API omission, so I turned to SO for ideas. And again, got two great ones. – akauppi Nov 08 '14 at 17:35
  • @AnasAlkhatib With your suggestion, I must use an `assert` instead of `should` matchers, right? I'd like to use matchers for the tests. – akauppi Nov 08 '14 at 18:18
  • You can always use `Set().subsetOf(Set()) shouldBe true`, but you lose the information about what went wrong. – Hosam Aly Aug 09 '16 at 20:11

3 Answers3

9

Bill Venners had this to say on the ScalaTest mailing list:

Yes, we didn't want to hold up the 2.0 release to add that, but have since added it. I believe we added it to master, though, not the 2.2.x branch. Regardless, the syntax looks like:

xSet should contain allElementsOf (ySet)

Link to the message.

akauppi
  • 17,018
  • 15
  • 95
  • 120
2

I don't think there's any good reason for this. You can remedy this with a pimp my class:

object ScalaTestUtils {
    import org.scalatest.words.ResultOfContainWord

    implicit class ResultOfContainWordImprovements[T](val contains: ResultOfContainWord[Seq[T]]) {
        def allOf(right: Seq[T]) = contains allOf(right.head, right.tail.head, right.tail.tail :_*)
    }
}

You should probably make this account for Seqs with fewer than 2 elements (for which this would fail).

Then, you can do:

import ScalaTestUtils._
Seq(1, 2, 3) should contain allOf Seq(1, 2)
Ben Reich
  • 16,222
  • 2
  • 38
  • 59
1

Another possible solution is:

import org.scalatest.Inspectors.forAll

forAll(list) { wanted should contain(_) }

Expect an error message similar to this:

scala> forAll(List(1, 2)) { List(1) should contain(_) }
org.scalatest.exceptions.TestFailedException: forAll failed, because:
  at index 1, List(1) did not contain element 2 (<console>:18)
in List(1, 2)
...
Caused by: org.scalatest.exceptions.TestFailedException: List(1) did not contain element 2
Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
  • Looks reasonable. However, ScalaTest seems to have released 3.0.0 so I presume the `should contain allElementsOf` that Bill mentions is there. Haven't checked, or needed it recently. – akauppi Aug 10 '16 at 08:13