使用带有 scalaz-scalacheck-binding 的 specs2 来测试定律
using specs2 with scalaz-scalacheck-binding to test laws
我发现在尝试使用 scalaz scalacheck 绑定库时,使用带有 scalacheck 的 specs2 来验证 Monoid 法则有点难看。
我的代码使用 scalaz Monoid,所以我想使用它们的法则来验证我的 MyType 是否实现了它们。
这种丑陋让我觉得我遗漏了什么或误用了 Specs2 或 scalacheck-binding 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
方法,只要传递的函数 returns a Result
其中 scalacheck 的 Prop
是一个有效的 Result
scalacheck-binding api 给了我一个 monoid.laws[T]
函数 returns 一个 Properties
这是一个 Prop
所以这应该没问题,它还采用 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
cannot be applied to (org.scalacheck.Properties)
它要求 Arbitrary 中的 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-bindings,它不那么难看,输出的信息让我相信法律已经过试验和测试?
谢谢
卡尔
你可以直接使用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 扩展规范 { 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
我发现在尝试使用 scalaz scalacheck 绑定库时,使用带有 scalacheck 的 specs2 来验证 Monoid 法则有点难看。 我的代码使用 scalaz Monoid,所以我想使用它们的法则来验证我的 MyType 是否实现了它们。
这种丑陋让我觉得我遗漏了什么或误用了 Specs2 或 scalacheck-binding 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
方法,只要传递的函数 returns a Result
其中 scalacheck 的 Prop
是一个有效的 Result
scalacheck-binding api 给了我一个 monoid.laws[T]
函数 returns 一个 Properties
这是一个 Prop
所以这应该没问题,它还采用 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
cannot be applied to (org.scalacheck.Properties)
它要求 Arbitrary 中的 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-bindings,它不那么难看,输出的信息让我相信法律已经过试验和测试?
谢谢
卡尔
你可以直接使用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 扩展规范 { 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