Specs2を試す(6)
Given/When/Then
Given/When/Then というスタイルでspecを書くこともできる。
// コード1 import org.specs2._ import org.specs2.specification._ import org.specs2.execute._ class GivenWhenThenSpec extends Specification { def is = "A given-when-then example for the addition" ^ "Given the following number: ${1}" ^ number1 ^ "And a second number: ${2}" ^ number2 ^ "Then I should get: ${3}" ^ result ^ end object number1 extends Given[Int] { def extract(text: String): Int = extract1(text).toInt } case class Addition(n1: Int, n2: Int) { def add: Int = n1 + n2 } object number2 extends When[Int, Addition] { def extract(number1: Int, text: String) = Addition(number1, extract1(text).toInt) } object result extends Then[Addition] { def extract(addition: Addition, text: String): Result = addition.add must_== extract1(text).toInt } }
number1がGiven。型パラメータのIntはextractメソッドが返す型。
Text部分で${}で括られた値はextractNというメソッドで取得することができる。
extractNはextract10まであって、extract2〜extract10についてははタプルを返してくる。コード2参照。
number2はWhen。ここではnumber1で取得した結果の型であるIntと、その結果と2つ目の数(${2}の部分、つまり2)から作りだしたAddition型が型引数となっている。
ここではextractメソッドの定義は
extract(Int, String): Addition
となる。
最後のresultがThenステップでここではAddition型とTextを引数にとってResultを返している。
// コード2 import org.specs2._ import org.specs2.specification._ import org.specs2.execute._ class GivenWhenThenSpec extends Specification { def is = "A given-when-then example for the addition" ^ "Given the following number: ${1}, ${2}, ${3}" ^ number1 ^ "And a second number: ${4}" ^ number2 ^ "Then I should get: ${10}" ^ result ^ end object number1 extends Given[Int] { def extract(text: String): Int = { extract3(text) match { case (x, y, z) => x.toInt + y.toInt + z.toInt } } } case class Addition(n1: Int, n2: Int) { def add: Int = n1 + n2 } object number2 extends When[Int, Addition] { def extract(number1: Int, text: String) = Addition(number1, extract1(text).toInt) } object result extends Then[Addition] { def extract(addition: Addition, text: String): Result = addition.add must_== extract1(text).toInt } }
Multiple Steps
Given/When/Thenは例えばG/W/W/T/TのようにWhenやThenが複数になってもよい。ただし
- 最初はGiven[T]
- Given[T]の次はWhen[T, S]かThen[T]
- When[T, S]の次はWhen[S, U]かThen[S]
- Then[S]の次はThen[S]
具体的には
- Given[T] / When[T, S] / Then[S]
- Given[T] / When[T, S] / Then[S] / Then[S]
- Given[T] / Then[T] / Then[T]
- Given[T] / When[T, S] / When[S, U] / Then[U]
Several G/W/T blocks
2つ以上のGiven/When/Thenを書きたいときは一度endで終わらせる。
"A given-when-then example for the addition" ^ "Given the following number: ${1}" ^ number1 ^ "And a second number: ${2}" ^ number2 ^ "Then I should get: ${3}" ^ addition ^ end^ // ☆ "A given-when-then example for the multiplication" ^ "Given the following number: ${1}" ^ number1 ^ "And a second number: ${2}" ^ number2 ^ "Then I should get: ${2}" ^ multiplication ^ end
User regexps
${} を使わずに自分で正規表現を書いて使うこともできる。
メリットはTextの部分がそのままReportに出てくれること、デメリットは同じようなものを2回書くことになるのでめんどくさいということ。
// コード3 import org.specs2._ import org.specs2.specification._ import org.specs2.execute._ class GivenWhenThenSpec extends Specification { def is = "A given-when-then example for the addition" ^ "Given the following number: 1" ^ number1 ^ "And a second number: ${2}" ^ number2 ^ "Then I should get: ${3}" ^ result ^ end object number1 extends Given[Int]("Given the following number: (.*)") { // 抽出用正規表現 def extract(text: String): Int = extract1(text).toInt } case class Addition(n1: Int, n2: Int) { def add: Int = n1 + n2 } object number2 extends When[Int, Addition] { def extract(number1: Int, text: String) = Addition(number1, extract1(text).toInt) } object result extends Then[Addition] { def extract(addition: Addition, text: String): Result = addition.add must_== extract1(text).toInt } }