代码之家  ›  专栏  ›  技术社区  ›  Karl

使用specs2和scalaz-scalacheck绑定到测试规则

  •  2
  • Karl  · 技术社区  · 8 年前

    我发现在尝试使用scalaz-scalacheck绑定库时,使用specs2和scalacheck来验证Monoid定律有点难看。 我的代码使用scalaz Monoid,所以我想使用他们的法律来验证我的MyType是否实现了它们。

    这种丑陋让我觉得我在使用Specs2或scalacheck绑定API时遗漏了什么或错误了。建议很准确。

    这就是我所做的:-

    我使用的是specs2 3.7和scalaz 2.7.0

    在“ http://etorreborre.github.io/specs2/guide/SPECS2-3.0/org.specs2.guide.UseScalaCheck.html " 我已经扩展了我的规格 Scalacheck 我有一个 Arbitrary[MyType] 所以我应该能够使用scalacheck OK。

    上面提到的文档指出,我需要向 prop 方法,只要传递的函数返回 Result scalacheck在哪里 Prop 是有效的 后果

    scalacheck绑定api为我提供了一个 monoid.laws[T] 返回 Properties 这是一个 道具 所以这应该是可以的,它还接受类型的隐式参数 Monoid[T] , Equal[T] Arbitrary[T] 所有这些都在我的范围内 T MyType

    我想这样做:

    class MyTypeSpec extends Specification with ScalaCheck {
      def is = s2"""
       MyType spec must :-
         obey the Monoid Laws $testMonoidLaws
      """
    
      def testMonoidLaws = {
        import org.scalacheck.{Gen, Arbitrary}
        import scalaz.scalacheck.ScalazProperties._
        implicit val arbMyType: Arbitrary[MyType] = genArbMyTpe() // an helper Arbitrary Gen func i have written
        prop { monoid.laws[MyType] }
      }
    }
    

    但是 道具 cannot be applied to (org.scalacheck.Properties) 它要求任意函数中的T是函数参数中的类型,所以我已经这样做了,注意我抹掉了参数T。。。

    class MyTypeSpec extends Specification with ScalaCheck {
      def is = s2"""
       MyType spec must :-
         obey the Monoid Laws $testMonoidLaws
      """
    
      def testMonoidLaws = {
        import org.scalacheck.{Gen, Arbitrary}
        import scalaz.scalacheck.ScalazProperties._
        implicit val arbMyType: Arbitrary[MyType] = genArbMyTpe() //some Arbitrary Gen func
        prop { (t: Path => monoid.laws[MyType] }
      }
    }
    

    我的考试通过了。耶!那么问题是什么?

    我对考试感到不安。它只说它通过了。如果使用Scalacheck直接告诉我它运行和通过了哪些法律,我不会得到任何输出。 我也扔掉了参数 t 并让 monoid.laws[MyType] 找到范围内的隐含含义,这似乎是错误的。它有效吗?我有没有弄错specs2 API?

    修改MyType,这样它肯定会失败,法律导致测试失败,这很好,但我仍然感到不安,因为它总是失败

    Falsified after 0 passed tests.
    

    我可以通过以下方式收集任意[MyType]

    prop { (p: Path) => monoid.laws[Path] }.collectArg(f => "it was " + f.shows)
    

    然后像这样运行

    sbt testOnly MyTypeSpec -- scalacheck.verbose
    

    它向我显示了 t型 当它起作用但当我扔掉 t型 我不确定这是否有效。

    有没有更好的方法来使用Specs2和scalaz scalacheck绑定进行测试,这种绑定不那么难看,并且输出的信息让我相信Laws已经过了测试?

    谢谢

    卡尔

    1 回复  |  直到 8 年前
        1
  •  3
  •   Eric    8 年前

    您可以使用 Properties 无需使用 prop 。下面是一个完整的示例:

    import org.specs2._
    import scalaz.scalacheck.ScalazProperties._
    import org.scalacheck._
    import scalaz._, Scalaz._
    import PositiveInt._
    
    class TestSpec extends Specification with ScalaCheck { def is = s2"""
    
     PositiveInt should pass the Monoid laws $e1
    
    """
      def e1 = monoid.laws[PositiveInt]
    }
    
    case class PositiveInt(i: Int)
    
    object PositiveInt {
      implicit def ArbitraryPositiveInt: Arbitrary[PositiveInt] =
        Arbitrary(Gen.choose(0, 100).map(PositiveInt.apply))
    
      implicit def EqualPositiveInt: Equal[PositiveInt] =
        Equal.equalA[PositiveInt]
    
      implicit def MonoidPositiveInt: Monoid[PositiveInt] = new Monoid[PositiveInt] {
        val zero = PositiveInt(1)
        def append(p1: PositiveInt, p2: =>PositiveInt): PositiveInt =
          PositiveInt(p1.i + p2.i)
      }
    }
    

    因为 Monoid 实例不正确,将失败:

    [info] TestSpec
    [info]
    [error]  x PositiveInt should pass the Monoid laws
    [error]  Falsified after 0 passed tests.
    [error]  > Labels of failing property:
    [error]  monoid.left identity
    [error]  > ARG_0: PositiveInt(3)
    [info]
    [info]
    [info] Total for specification TestSpec
    [info] Finished in 185 ms
    [info] 1 example, 1 failure, 0 error
    

    失败表明第一部法律未能通过。然而,它并没有创建几个例子,每个法律一个,来显示哪个法律正在执行。如果你想这样做,你可以绘制法律的每个属性 属性 例如: class TestSpec使用ScalaCheck扩展规范{def is=s2“”

     PositiveInt should pass the Monoid laws $properties
    
    """
    
      def properties = toExamples(monoid.laws[PositiveInt])
    
      def toExamples(ps: Properties): Fragments =
        t ^ Fragments.foreach(ps.properties) { case (name, prop) => br ^ name ! prop }
    }
    

    此打印(用于传递 Monoid[PositiveInt] 实例):

    [info] TestSpec
    [info]
    [info]  PositiveInt should pass the Monoid laws
    [info]   + monoid.semigroup.associative
    [info]   + monoid.left identity
    [info]   + monoid.right identity
    [info]
    [info] Total for specification TestSpec
    [info] Finished in 91 ms
    [info] 3 examples, 300 expectations, 0 failure, 0 error