ScalaCheck を試す [Scala]
The fun of programming の第2章は QuickCheck という Haskell 用のテスト支援ツールの紹介。Scala にも QuickCheck を参考にした ScalaCheck [1] というのがあるので触ってみることにした。
* インストール
sbaz をセットアップしてあれば sbaz install scalacheck とするか、よくわからなければ ScalaCheck-0.2.jar をダウンロードしてきて Scala の lib ディレクトリに おけばよい。
* 使ってみる
インタプリタ上で使うことにします。まずは以下の名前をインポート。
scala> import scalacheck._ import scalacheck._ scala> import scalacheck.Gen._ import scalacheck.Gen._ scala> import scalacheck.Prop._ import scalacheck.Prop._ scala> import scalacheck.Test._ import scalacheck.Test._
加算演算子について「結合法則を満たす」という性質をテストしたい場合は以下のように仕様を書きます。
scala> val propAssoc = property( (x:Int, y:Int, z:Int) => (x + y) + z == x + (y + z) ) propAssoc: scalacheck.Gen[scalacheck.Prop.PropRes] = scalacheck.Gen$$anon$0@1fe500a
これをテストするには check メソッドを使います。
scala> check(propAssoc) +++ OK, passed 100 tests. res0: scalacheck.TestStats = TestStats(TestPassed(),100,0)
これで自動的にテストデータを100件作ってテストをしてくれました。(テストされたデータをログとして残すことはできないのだろうか?)
本ではこれを整数でなく浮動小数点数にすると丸め誤差でテストを通らなくなる!というのが面白かったのですが ScalaCheck には任意の浮動小数点数データを生成してくれる機能は今のところ無い模様。
今度は与えられた整数の桁数を求める関数 (cf. [2]) を以下のように定義してみます。
scala> def len(x: Int) = Math.floor(Math.log(x) / Math.log(10)).toInt + 1 len: (Int)Int
この関数はきっと次の性質を満たすことでしょう。
scala> val propLen = property ( (x:Int) => len(x) == x.toString.length ) propLen: scalacheck.Gen[scalacheck.Prop.PropRes] = scalacheck.Gen$$anon$0@3da1dc
テストしてみます。
scala> check(propLen) *** Failed, after 0 successful tests: The arguments that caused the failure was: List(0) res4: scalacheck.TestStats = TestStats(TestFailed(List(0)),0,0)
失敗しました。List の内容が失敗する場合の引数の例のようで、この関数は引数が 0 の時は失敗するということです。あー、本当はそういう仕様かも、ということで仕様のほうを変えてしまいます。「引数が 0 より大きい場合は」というような前提条件をつけるには ==> を使います(演算子の優先順位に注意)。
scala> val propLen = property ( (x:Int) => x > 0 ==> (len(x) == x.toString.length) ) propLen: scalacheck.Gen[scalacheck.Prop.PropRes] = scalacheck.Gen$$anon$0@1bf446e scala> check(propLen) +++ OK, passed 100 tests. res5: scalacheck.TestStats = TestStats(TestPassed(),100,123)
今度はテストを通りました。実はこれも log の誤差で失敗してほしかったのですが
追記: x が 1000 のときにちゃんと?誤差が出るようになっていました。check(propLen) は何度やっても成功するのでたまたま当たらないということでしょうか。なんかいきなり ScalaCheck の限界を見てしまったような…
続くかも。
[1] http://code.google.com/p/scalacheck/
[2] http://ja.doukaku.org/40/
コメント 0