Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to avoid mutable state using Cucumber-jvm Scala?

With the Cucumber tests, a feature expressed as Given, When and Then is usually implemented as three separate methods. These methods often need to share values, and this it seems that mutable variables are the way to do it.

Take the following simple example:

A feature:

Given the digit 2
When it is multiplied by 3
Then the result is 6

And the Cucumber methods:

class CucumberRunner extends ScalaDsl with EN with ShouldMatchers {

  var digitUnderTest: Int = -1

  Given("""^the digit (\d)$""") { digit: Int =>
    digitUnderTest = digit
  }

  When("""^it is multiplied by 3$""") {
    digitUnderTest = digitUnderTest * 3
  }

  Then("""^the result is (\d)$""") { result: Int =>
    digitUnderTest should equal (result)
  }
}

Is there any way, presumably built into Scala test or Cucumber-jvm for Scala, that allows me not to express digitUnderTest as a mutable variable?

like image 735
Noel M Avatar asked Jan 18 '13 18:01

Noel M


1 Answers

Looking at cucumber-jvm examples in java and scala, I doubt it provides a way to passing data from step to step without storing it in a variable temporarily.

Since you cannot reassign a val in scala, the closest thing I can think of for you to get rid of the mutable var is to have a global map that holds temporary test data.

class CucumberRunner extends ScalaDsl with EN with ShouldMatchers {

  Given("""^the digit (\d)$""") { digit: Int =>
    GlobalTestData.save("my_unique_key_1", digit)
  }

  When("""^it is multiplied by 3$""") {
    GlobalTestData.load("my_unique_key_1") match {
      case Some(obj) => {
        val result = obj.asInstanceOf[Int] * 3
        GlobalTestData.save("my_unique_key_2", result)
      }
      case None => // throw exception or fail test
    }
  }

  Then("""^the result is (\d)$""") { result: Int =>
    GlobalTestData.load("my_unique_key_2") match {
      case Some(obj) => obj.asInstanceOf[Int] should equal (result)
      case None => // throw exception or fail test
    }
  }
}

And then the GlobalTestData:

object GlobalTestData {
  val map = scala.collection.mutable.Map.empty[String, Any];

  def save(key: String, value: Any) {
    map.put(key, value)
  }

  def load(key: String): Option[Any] = map.get(key)
}

In this case, you need to carefully generate keys so they are the same across steps. Of course, you can use some vals to hold the values of these keys.

Also in this particular feature, why not combine Given and When steps:

When the digit 2 is multiplied by 3
Then the result is 6

This way you can save one slot in GlobalTestData.

like image 97
zihaoyu Avatar answered Sep 29 '22 00:09

zihaoyu