Matt
Matt

Reputation: 893

Using "contains" matcher on Scala lists in Scala test

I'm trying to check that a list of case classes contains a specific instance of one, however when I attempt to do so I get the following error:

[info] Compiling 1 Scala source to /home/matt/Documents/transledge/app/target/scala-2.9.2/test-classes...
[error] /home/matt/Documents/transledge/app/src/test/scala/com/transledge/drewes/parser_suite.scala:40: overloaded method value should with alternatives:
[error]   (notWord: ParserSuite.this.NotWord)ParserSuite.this.ResultOfNotWordForSeq[com.transledge.Instruction,List[com.transledge.Instruction]] <and>
[error]   (haveWord: ParserSuite.this.HaveWord)ParserSuite.this.ResultOfHaveWordForSeq[com.transledge.Instruction] <and>
[error]   (beWord: ParserSuite.this.BeWord)ParserSuite.this.ResultOfBeWordForAnyRef[List[com.transledge.Instruction]] <and>
[error]   (rightMatcher: org.scalatest.matchers.Matcher[List[com.transledge.Instruction]])Unit
[error]  cannot be applied to (org.scalatest.matchers.Matcher[Traversable[com.transledge.AddNode]])
[error]       parsing(square_node, input) should contain(AddNode("foo"))
[error]                                   ^
[error] one error found
[error] (test:compile) Compilation failed
[error] Total time: 3 s, completed 21-Feb-2013 15:15:04

The test suite in question is:

import org.scalatest.FunSpec
import scala.util.parsing.combinator._
import com.transledge.drewes.{Parser => DrewesParser}
import com.transledge._
import org.scalatest.matchers.ShouldMatchers

class ParserSuite extends DrewesParser with FunSpec with ShouldMatchers {

  def parsing[A](parser: Parser[A], input: String): A = parse(parser, input).get

  // snipping other tests

  describe("square_node") {
    val input = """\squarenode{foo}(1cm, 2cm)"""
    it("should create a node") {
      parsing(square_node, input) should contain(AddNode("foo")) // Line 40
    }
  }
}

The definition of AddNode/Instruction is pretty basic:

package com.transledge

abstract class Instruction
case class AddNode(id: String) extends Instruction

And here is a cut down definition of the parser:

package com.transledge.drewes
import scala.util.parsing.combinator._
import com.transledge._

trait Parser extends RegexParsers {
  def node_id: Parser[String] = "[a-zA-Z\\-_:0-9]+".r
  def node_name: Parser[String] = ("{" ~> node_id <~ "}") | node_id

  def point: Parser[String] = "[^,()]+".r
  def position: Parser[(String, String)] = "(" ~> point ~ "," ~ point <~ ")" ^^ { case a ~ "," ~ b => (a.trim, b.trim) }

  def square_node: Parser[List[Instruction]] = "\\squarenode" ~> node_name ~ position ^^ { case name ~ position => List(AddNode(name)) }

}

My understanding of this is that the Scala compiler should be using the variation should(rightMatcher: Matcher[List[T]]), but is getting an instance of Traversable instead of a List, and as Traversable is a trait that List includes, Traversable can't be used in a place that List is expected.

So how do I check that the list contains the element?

Upvotes: 3

Views: 3176

Answers (1)

EECOLOR
EECOLOR

Reputation: 11244

This is a simplified picture of how it's implemented:

trait Matcher[T]

implicit class ListShouldWrapper[T](a:List[T]) {
  def should(rightMatcher: Matcher[List[T]]): Unit = ???
}

object contain {
  def apply[T](expectedElement: T): Matcher[GenTraversable[T]] = ???
}

If you test that implementation with:

val x:List[Int] = ???
x should contain(3)

You will get a compile error telling you that GenTraversable was found and List was required. The implementation might have worked better if we implemented it something like this the following. Note that this is not the actual solution as this is just some isolated piece of code.

trait Matcher[T]

implicit class AnyToShould[T](a: T) {
  def should(a: Matcher[T]) = ???
}

def contain[C[_] <: Traversable[_], T](x:T):Matcher[C[T]] = ???

The problem is solvable so I suggest you file a bug (maybe even create a patch yourself). To be able to continue now you have a few options:

  • Change the return type of def square_node: Parser[List[Instruction]]
  • Supply a contain method yourself that returns the 'correct' type of Matcher
  • Use another Scala specification library

Upvotes: 6

Related Questions