Scalacheck - 向命令添加参数

Scalacheck - Add parameters to commands

在用于状态测试的 Scalacheck documentation 中,提到了 ATM 机作为用例。为了使其工作,命令需要参数,例如 PIN 或取款金额。在给定的示例中,class Counter 中的方法没有参数。

现在我的问题是如何在 scalachecks 状态测试中测试这样的方法:

class Counter {
    private var n = 0
    def inc(i: Int) = n += i
    ...
}

命令的 runnextState 方法不提供参数。添加 Random.nextInt 是行不通的,因为在 runnextState 中值会不同并且测试失败:

case object Inc extends UnitCommand {
    def run(sut: Sut): Unit = sut.inc(Random.nextInt)

    def nextState(state: State): State = state + Random.nextInt
    ...
}

有什么方法可以将参数传递给 Sut

正如您可能从 genCommand 中注意到的那样,ScalaCheck Commands 实际上在 genInitialState 生成的初始状态和 [=11= 生成的一系列命令之间执行类似于笛卡尔积的操作].因此,如果您的某些命令实际上需要一个参数,则需要将它们从对象转换为 classes 并为它们提供一个 Gen。所以修改文档中的示例你需要这样的东西:

/** A generator that, given the current abstract state, should produce
  * a suitable Command instance. */
def genCommand(state: State): Gen[Command] = {
  val incGen = for (v <- arbitrary[Int]) yield Inc(v)
  val decGen = for (v <- arbitrary[Int]) yield Dec(v)
  Gen.oneOf(incGen, decGen, Gen.oneOf(Get, Reset))
}

// A UnitCommand is a command that doesn't produce a result
case class Inc(dif: Int) extends UnitCommand {
  def run(sut: Sut): Unit = sut.inc(dif)

  def nextState(state: State): State = state + dif

  // This command has no preconditions
  def preCondition(state: State): Boolean = true

  // This command should always succeed (never throw an exception)
  def postCondition(state: State, success: Boolean): Prop = success
}

case class Dec(dif: Int) extends UnitCommand {
  def run(sut: Sut): Unit = sut.dec(dif)

  def nextState(state: State): State = state - dif

  def preCondition(state: State): Boolean = true

  def postCondition(state: State, success: Boolean): Prop = success
}

请注意,如果您的参数只是常量而不是变量(如 PIN 码的情况),您应该在命令中对它们进行硬编码或制定整个规范 class 而不是对象并从外部传递这些参数。