如何让ScalaCheck的Arbitrary始终生成一些特殊的案例值?

时间:2011-09-27 12:55:53

标签: scala scalacheck

除了一些随机值之外,我还希望我的所有属性都至少使用一组固定的特殊值进行测试。我想在我的生成器规范中定义它,而不是在使用该生成器类型的每个测试中。例如,如果我生成Ints,我希望我的生成器始终为每个测试用例生成至少0,1和-1。这可能吗?

到目前为止,我提出的最好的方法是制作一个大小的生成器,其中最小的 n 大小对应于我的 n 特殊情况。这是有问题的,至少因为当最大测试次数配置为低于最大尺寸参数时,不会测试所有可能的尺寸。

2 个答案:

答案 0 :(得分:17)

首先,已经在Scalacheck中存在偏差,因此除了以外很可能会选择0,1,-1,Int.MaxValueInt.MinValue其他Int值。所以,如果那是您的担心,请不要担心。同样,可能会生成空字符串。

但是,如果您想要为其他内容重现此行为,请使用Gen.oneOfGen.frequency,也许与Gen.choose结合使用。由于oneOffrequencyGen作为参数,因此您可以将特殊情况与通用生成器结合使用。

例如:

val myArb: Arbitrary[Int] = Arbitrary(Gen.frequency(
    1 -> -1, 
    1 ->  0, 
    1 -> 1, 
    3 -> Arbitrary.arbInt.arbitrary
))

你所要求的几乎是你所要求的,有50%几率任意一次(这与我所说的偏见有关),以及-1,0和1各有16.6%。

答案 1 :(得分:1)

我今天有同样的问题,并在这里结束,以为我要添加我的解决方案,即在使用Prop之前先生成Gen个特殊情况,如下所示:

import org.scalacheck.Gen.{alphaChar, const}
import org.scalacheck.Prop.{forAll, passed}
import org.scalacheck.{Gen, Prop}

// evaluate fn first with some initial values, then with some generated ones
def forAllAfter[A](init: A*)(subsequent: Gen[A])(fn: A => Prop): Prop =
  init.foldLeft(passed) { case (p, i) => p && forAll(const(i))(fn) } && forAll(subsequent)(fn) 

// example of usage
val prop = forAllAfter('a', 'b', 'c')(alphaChar) { c =>
  println(c) 
  passed
}

此处的forAllAfter函数首先使用Prop为每个必须测试的值创建Gen.const,然后将它们与使用后续值生成器进行测试的道具组合。

如果您使用的是ScalaTest,则需要将Checkers特质混入测试中以评估结果Prop,如下所示:

import org.scalatest.WordSpec
import org.scalatest.prop.Checkers
import org.scalacheck.Gen.{alphaChar, const}
import org.scalacheck.Prop.{forAll, passed}
import org.scalacheck.{Gen, Prop}

class TestExample extends WordSpec with Checkers {

  def forAllAfter[A](init: A*)(subsequent: Gen[A])(fn: A => Prop): Prop =
    init.foldLeft(passed) { case (p, i) => p && forAll(const(i))(fn) } && forAll(subsequent)(fn)

  val prop: Prop = forAllAfter('a', 'b', 'c')(alphaChar) { c =>
    println(c)
    passed
  }

  "Test example" should {
    "Work correctly" in {
      check(prop)
    }
  }
}