0

In the Scalacheck documentation for stateful testing an ATM maschine is mentioned as a use case. For it to work, the commands need parameters, for example the PIN or the withdrawal amount. In the given example the methods in the class Counter don't have parameters.

Now my question is how I could test a method like this in scalachecks stateful testing:

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

The run and nextState methods of a command don't offer a parameter. Adding a Random.nextInt wouldn't work, because in run and nextState the value would differ and the test fails:

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

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

Is there any way to pass a parameter to the Sut?

0

1 Answer 1

1

As you may notice from how genCommand, ScalaCheck Commands actually does something like a Cartesian product between initial states generated by genInitialState and series of commands generated by genCommand. So if some of your commands actually need a parameter, you need to convert them into classes from objects and provide a Gen for them. So modifying the example from the docs you will need something like this:

/** 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
}

Note that if your parameters are just constants rather than variables (as is in the case of PIN-code), you should either hard-code them in the commands or make the whole specification class rather than object and pass those parameters from the outside.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, this looks like the solution I was looking for!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.