与specs2匹配的地图

时间:2014-10-22 12:08:09

标签: scala specs2

假设我有一个函数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 - 匹配器来匹配对值。它有意义吗?

1 个答案:

答案 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

  }

}