Specs2を試す(7)

ScalaCheck values

Given/When/Then シーケンスで scalacheck の generator を使う方法。

import org.scalacheck._
import org.scalacheck.Gen._
import org.scalacheck.Prop._
import org.specs2._
import org.specs2.specification.gen._


class GivenWhenThenScalacheckSpec extends Specification with ScalaCheck { def is =

  "A given-when-then example for a calculator"                                   ^
    "Given a first number n1"                                                    ^ number1 ^
    "And a second number n2"                                                     ^ number2 ^
    "When I add them"                                                            ^ add ^
    "Then I should get n1 + n2"                                                  ^ result ^
    end

  object number1 extends Given[Int] {
    def extract(text: String) = choose(-10, 10)
  }
  object number2 extends When[Int, (Int, Int)] {
    def extract(number1: Int, text: String) = for { n2 <- choose(-10, 10) } yield (number1, n2)
  }
  object add extends When[(Int, Int), Addition] {
    def extract(numbers: (Int, Int), text: String) = Addition(numbers._1, numbers._2)
  }
  object result extends Then[Addition] {
    def extract(text: String)(implicit op: Arbitrary[Addition]) = {
      check { (op: Addition) => op.calculate must_== op.n1 + op.n2 }
    }
  }

  case class Addition(n1: Int, n2: Int)  { def calculate: Int = n1 + n2 }
}


通常はGiven/When/Thenのステップクラスはorg.specs2.specificationの下のものをimportして使うけれど
scalacheckを使うときはorg.specs2.specification.gen._ のものを使う。


number1, number2 の extract メソッドは scalacheck の generator(Gen[T]) という型を返している。
一方、add の extract メソッドは Addition型を返しているが、T から Gen[T] への暗黙変換が効く。
ちなみにこれは specs2 ではなく scalacheck のほうに定義してある。


ScalaCheck トレイトは check メソッドを使うために mixin されている。
check メソッドに関数を渡すとその結果が scalacheck の Prop 型として返ってくる。
それがさらに specs2 の Result 型に変換される仕組み。


Then の extract メソッドには implicit な Arbitrary[T] の引数があるが、これは
ScalaCheck 側でプロパティを生成するのに使っている。この変の仕組みは specs2 ではなく scalacheck のほうを見たほうが良い。


とまあこれだけを理解するのにspecs2とscalacheckのソースと3時間くらいにらめっこしました。修行が足りない。