Scalacheck无法正确报告失败案例

Lor*_*Goo 9 automated-tests unit-testing scala scalatest scalacheck

我写了以下规范

"An IP4 address" should "belong to just one class" in {
    val addrs = for {
        a <- Gen.choose(0, 255)
        b <- Gen.choose(0, 255)
        c <- Gen.choose(0, 255)
        d <- Gen.choose(0, 255)
    } yield s"$a.$b.$c.$d"

    forAll (addrs) { ip4s =>
        var c: Int = 0
        if (IP4_ClassA.unapply(ip4s).isDefined) c = c + 1
        if (IP4_ClassB.unapply(ip4s).isDefined) c = c + 1
        if (IP4_ClassC.unapply(ip4s).isDefined) c = c + 1
        if (IP4_ClassD.unapply(ip4s).isDefined) c = c + 1
        if (IP4_ClassE.unapply(ip4s).isDefined) c = c + 1
        c should be (1)
    }
}
Run Code Online (Sandbox Code Playgroud)

这范围非常明确.

测试成功通过,但是当我强制它失败时(例如通过注释掉其中一个if语句),ScalaCheck会正确报告错误,但消息没有正确提及用于评估命题的实际值.更具体地说,我得到:

[info] An IP4 address
[info] - should belong to just one class *** FAILED ***
[info]   TestFailedException was thrown during property evaluation.
[info]     Message: 0 was not equal to 1
[info]     Location: (NetSpec.scala:105)
[info]     Occurred when passed generated values (
[info]       arg0 = "" // 4 shrinks
[info]     )
Run Code Online (Sandbox Code Playgroud)

你可以看到的地方arg0 = "" // 4 shrinks没有显示价值.

我试图添加一个简单的println声明来审查案例,但输出似乎被修剪.我得到这样的东西

192.168.0.1
189.168.
189.
1
Run Code Online (Sandbox Code Playgroud)

import org.scalacheck.Prop.forAllNoShrink
import org.scalatest.prop.Checkers.check

"An IP4 address" should "belong to just one class" in {
  val addrs = for {
    a <- Gen.choose(0, 255)
    b <- Gen.choose(0, 255)
    c <- Gen.choose(0, 255)
    d <- Gen.choose(0, 255)
  } yield s"$a.$b.$c.$d"
  check {
    forAllNoShrink(addrs) { ip4s =>
      var c: Int = 0
      if (IP4.ClassA.unapply(ip4s).isDefined) c = c + 1
      if (IP4.ClassB.unapply(ip4s).isDefined) c = c + 1
      if (IP4.ClassC.unapply(ip4s).isDefined) c = c + 1
      if (IP4.ClassD.unapply(ip4s).isDefined) c = c + 1
      if (IP4.ClassE.unapply(ip4s).isDefined) c = c + 1
      c == (1)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Ric*_*son 9

这是由ScalaCheck的测试用例简化功能引起的.ScalaCheck只看到你的生成器产生一个字符串值.只要它找到使您的属性为false的值,它就会尝试简化该值.在你的情况下,它简化了四次,直到它结束一个空字符串,仍然使你的属性为假.

所以这是预期的,虽然令人困惑,但行为.但是你可以用三种不同的方式来改善这种情况.

您可以选择其他数据结构来表示您的IP地址.这将使ScalaCheck能够以更智能的方式简化您的测试用例.例如,使用以下生成器:

val addrs = Gen.listOfN(4, Gen.choose(0,255))
Run Code Online (Sandbox Code Playgroud)

现在,ScalaCheck知道您的生成器只生成长度为4的列表,并且它只包含0到255之间的数字.测试用例简化过程将考虑到这一点而不创建任何由生成器无法生成的值.开始.您可以在您的属性内转换为字符串.

第二种方法是直接向您的生成器添加一个过滤器,告诉ScalaCheck IP地址字符串应该是什么样子.在测试用例简化期间使用此过滤器.定义一个检查有效字符串的函数,并以这种方式将其附加到现有生成器:

def validIP(ip: String): Boolean = ...

val validAddrs = addrs.suchThat(validIP)

forAll(validAddrs) { ... }
Run Code Online (Sandbox Code Playgroud)

第三种方法是通过使用forAllNoShrink而不是完全禁用测试用例简化功能forAll:

Prop.forAllNoShrink(addrs) { ... }
Run Code Online (Sandbox Code Playgroud)

我还要提一下,前两种方法需要ScalaCheck版本> = 1.11.0才能正常运行.

更新:

listOfN表的长度实际上没有收缩器的尊重更多,由于https://github.com/rickynils/scalacheck/issues/89.希望这可以在ScalaCheck的未来版本中修复.