【问题标题】:using specs2 with scalaz-scalacheck-binding to test laws使用带有 scalaz-scalacheck-binding 的 specs2 来测试法律
【发布时间】:2016-01-06 08:55:27
【问题描述】:

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

这种丑陋让我觉得我遗漏了一些东西或误用了 Specs2 或 scalacheck-binding API。建议赞赏。

这就是我所做的:-

我正在使用带有 scalaz 2.7.0 的 specs2 3.7

在“http://etorreborre.github.io/specs2/guide/SPECS2-3.0/org.specs2.guide.UseScalaCheck.html”阅读用户指南 我已经用Scalacheck trait 扩展了我的规范,并且我在范围内有一个Arbitrary[MyType],所以我应该能够使用scalacheck OK。

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

scalacheck-binding api 给了我一个monoid.laws[T] 函数,它返回一个Properties 这是一个Prop 所以这应该没问题,它还接受Monoid[T]Equal[T] 和@987654333 类型的隐式参数@ 我在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] }
  }
}

但是propcannot 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 进行测试,并且输出的信息让我相信 Laws 已经过尝试和测试?

谢谢

卡尔

【问题讨论】:

    标签: scalaz specs2 scalacheck


    【解决方案1】:

    您可以直接使用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 的每个属性映射到一个示例: 类 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
    

    【讨论】:

    • 谢谢埃里克。我不清楚我是否可以直接从所有示例都使用 prop 的文档中返回 Properties。谢谢
    猜你喜欢
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    相关资源
    最近更新 更多