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

标签 scalaz specs2 scalacheck

我发现在尝试使用 scalaz scalacheck 绑定(bind)库时,使用 scalacheck 的 specs2 来验证 Monoid 定律有点难看。 我的代码使用 scalaz Monoid,因此我想使用它们的定律来验证我的 MyType 是否实现了它们。

这种丑陋让我觉得我遗漏了一些东西或者误用了 Specs2 或 scalacheck 绑定(bind) 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-binding api 给了我一个 monoid.laws[T] 函数,它返回一个 Properties ,它是一个 Prop 所以这应该没关系,它还采用 Monoid[T]Equal[T]Arbitrary[T] 类型的隐式参数,所有这些我都有在 TMyType

的范围内

我想这样做:

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 不能应用于(org.scalacheck.P​​roperties) 它要求 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.

我可以通过以下方式收集 Arbitrary[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 - 使用 specs2 和 scalaz-scalacheck-binding 来测试法律,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34629024/

相关文章:

java - sbt 测试编码问题

scala - 规范 2:使用 Hamcrest 匹配器

scala - 使 ScalaCheck 测试具有确定性

scala - 如何显示 ScalaCheck 测试中抛出的异常的整个堆栈跟踪?

scala - 使用 scalacheck 生成任意(合法)Unicode 字符?

scala - Specs2 和 scalacheck - 必须通过问题

scala - 使用 Scalaz(或 Shapeless)为每个子类创建 Monoid

Scala:将 map 列表与每个键的最大值合并的惯用方法?

scala - 泛型中奇怪的嵌套结构类型

scala - 可折叠 "foldMap"采取部分功能 : foldCollect?