使用specs2和scalaz-scalacheck绑定来测试法律

时间:2016-01-06 08:55:28

标签: scalaz specs2 scalacheck

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

这种丑陋让我觉得我遗漏了一些东西或者误用了Specs2或scalacheck-binding API。 Sugestions apreciated。

这就是我所做的: -

我正在使用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确定。

上面提到的文档声明我需要将函数传递给prop方法,只要传递的函数返回Result,其中scalacheck的Prop是有效的Result }

scalacheck-binding api给了我一个monoid.laws[T]函数,它返回一个Properties Prop所以这应该没问题,它还需要{{1}类型的隐式参数} {,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] } } } prop 它要求任意中的T作为函数参数的类型,所以我已经这样做了,注意我扔掉了参数t,...

cannot be applied to (org.scalacheck.Properties)

我的测试通过。好极了!那有什么问题?

我对测试感到不安。它说它通过了。如果使用Scalacheck直接告诉我它运行和通过哪些法则,我就不会得到任何输出。 此外,我扔掉参数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] } } } ,让t找到范围内的含义,这似乎是错误的。它有效吗?我破坏了specs2 API吗?

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

monoid.laws[MyType]

我可以通过

收集任意[MyType]
Falsified after 0 passed tests.

然后像这样运行

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

它显示了sbt testOnly MyTypeSpec -- scalacheck.verbose 收集后的值{但是当我扔掉t时我不确定这是否有效。

有没有更好的方法来测试使用Specs2和scalaz scalacheck-bindings那些不那么难看并输出信息让我对Laws的尝试和测试充满信心?

由于

卡尔

1 个答案:

答案 0 :(得分:3)

您可以直接使用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

失败表明第一部法律未能通过。然而,它并没有创建几个例子,每个法律都有一个例子来显示正在执行的法律。如果您想这样做,您可以将法律Properties的每个属性映射到一个示例:     class TestSpec使用ScalaCheck扩展Specification {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