我发现使用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的尝试和测试充满信心?
由于
卡尔
答案 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