假设我有一个函数foo: () -> Map[String, A]
,其中A
是特征。
trait A { def x: Int, def y: Int }
现在我需要编写一个specs2
规范,以确保foo
返回一个包含两个预期对的地图。请注意,我不知道值的实际类型。
不幸的是,在这种情况下我没有弄清楚如何使用havePairs匹配器,所以我写的是:
"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)
}
这段代码很难看。你会怎么建议改变它?
我想我应该为A
和新的havePairs
匹配器编写一个自定义匹配器,它将使用此A
- 匹配器来匹配对值。它有意义吗?
答案 0 :(得分:2)
通常您会使用 havePairs ,因为使用了Traits,您无法使用基于 hashCode 的常规包含方法。< / p>
Scala不支持开箱即用的结构平等进行类比较。如果您使用案例类,您将获得该功能并可以使用 havePairs 。
如果你需要支持Traits,我担心你不得不为你想要支持和测试的每个案例编写Equal意义的代码。
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
}
}