Suppose I have a function foo: () -> Map[String, A]
, where A
is trait.
trait A { def x: Int, def y: Int }
Now I need to write a specs2
spec to make sure that foo
returns a map with two expected pairs. Note that I don't know the actual type of the values.
Unfortunately I didn't figure out how to use havePairs matcher in this case, so I am writing:
"test foo" in {
val m = foo()
def test(a: A, x: Int, y: Int) = (a.x must_== 0) and (a.y must_== 1)
test(m("key1"), 0, 1)
test(m("key2"), 3, 5)
}
This code is ugly. How would you suggest change it?
I guess I should write a custom matcher for A
and new havePairs
matcher, which would use this A
-matcher to match the pairs values. Does it make sense ?
Normally you would use havePairs because of the use of Traits you can not use the normal contains method which is based on hashCode.
Scala supports no structural Equality out of the box for class comparission. If you use case classes you get that feature and could use the havePairs.
If you need to support Traits I fear you are stuck with having to code what being Equal means for each individual case you want to support and test.
class ExampleSpec extends Specification{override def is: Fragments = s2"""
Generated map contains data $caseClassTest
works also for Trait $traitTest
"""
trait A { def x: Int ; def y: Int }
// case class, exactly the same as Trait A
case class CaseA(x: Int, y:Int) extends A
// generates a Map with Traits on calling apply on function
def generateTraitMap : () => Map[String,A] = () => Map(
"k1" -> new A{ def x = 1 ; def y = -1 },
"k2" -> new A{ def x = 0 ; def y = 42 }
)
// generates a Map with CaseClasses on calling apply on function
def generateCaseClassMap : () => Map[String,A] = () => Map(
"k1" -> CaseA(1, -1),
"k2" -> CaseA(0,42)
)
// Test with case classes works with havePairs out of the box
def caseClassTest = generateCaseClassMap() must havePairs(
"k1" -> CaseA(1, -1),
"k2" -> CaseA(0,42)
)
// testing Traits needs some plumbing, and will give poor error feedback
def traitTest = {
val data = generateTraitMap()
def testStructuralEquality(a: A, b: A) =
List[A => Int]( _.x, _.y ).forall(f => f(a) == f(b) )
val testData = Map(
"k1" -> new A{ def x = 1 ; def y = -1 },
"k2" -> new A{ def x = 0 ; def y = 42 }
)
testData.forall{ case (k,v) => testData.contains(k) && testStructuralEquality(v, testData(k)) } must beTrue
}
}