2014-10-22 103 views
1

假设我有一个功能foo:() -> Map[String, A],其中A是特质。与规格2匹配映射

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 -matcher相匹配的对数值。是否有意义 ?

+0

测试(图(“键1),0,1)是没有有效的Scala代码。你错过的东西,而粘贴?定义** VAL地图**是危险的,因为它可以得到混合了经常使用的功能** map **。 – 2014-10-22 13:48:47

+0

@AndreasNeumann Thanks。Fixed。 – Michael 2014-10-22 13:49:45

+0

您想让测试适用于*特质A *还是意味着要成为*类型参数*? – 2014-10-22 14:20:21

回答

2

通常你会使用havePairs因为使用性状的可以正常使用的包含基于的hashCode方法。

斯卡拉不支持类比较的结构性开箱即用。如果您使用案例类别您可以使用该功能并可以使用havePairs

如果您需要支持特性,我担心您不得不编码什么是等号意味着您要支持和测试的每个个案。

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 

    } 

}