7

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?

Noel M
  • 15,812
  • 8
  • 39
  • 47
  • Have you considered using specs2's immutable [Given/When/Then structure](http://etorreborre.github.com/specs2/guide/org.specs2.guide.Structure.html#G%2FW%2FT)? – Ratan Sebastian Jan 18 '13 at 21:38

2 Answers2

2

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.

zihaoyu
  • 5,483
  • 11
  • 42
  • 46
  • Thanks for answering - I did consider something like that, but ideally I don't want anything mutable at all. – Noel M Jan 18 '13 at 22:47
  • And this feature was just an example, combining Given and When doesn't make much sense for my real steps. – Noel M Jan 18 '13 at 22:48
0

Although in the ScalaDSL for CucumberJVM, a step is a function f:List[Any] => Any, the current implementation discards the results the each step execution, meaning that you have no way to use the result of a previous step in the next one.

Currently, the only way to share the result of one step is through some shared state, either at the step definition class or a more global context like zihaoyu suggested (BTW, we use the shared mutable map method as well in a large project)

krismath
  • 1,879
  • 2
  • 23
  • 41
maasg
  • 37,100
  • 11
  • 88
  • 115