読者です 読者をやめる 読者になる 読者になる

taketoncheir.log

Like the Decatoncheir by Poseidon Industrial, This blog is Yet Another Storage for My Long Term Memories.

QuickCheckテストの信頼度チェック

この記事はHaskell Advent Calendar 2012用の記事です。

今年はテストに関わる記事が多いように思います。
継続的な開発を行う上でテストは必要なので、テスト手法をキャッチアップしたいと思っていたところでした。まるで渡りに船、いや、ほむらにまどかです。

(追記)

  • QuickCheckで自分で定義したデータ型に対してテストを書きたい場合はこちら
  • QuickCheckでデータがランダムに生成される仕組みについてはこちら

QuickCheck

テストを書く際、QuickCheckで性質のチェックも行うと思います。
QuickCheckは、

"QuickCheck is property-based tool for random testing."
(Arts, 2005) pdf download

なるものです。
テスト対象の関数が満たすべき性質を記述し、QuickCheckに渡すと、ランダムな値を生成、その性質が各値に対して成り立つかどうかをチェックしてくれます。
(注:QuickCheckについてのチュートリアルは、RWHが一番まとまっていたように思います。)

しかし、QuickCheckさん、実行したら

Ok, passed 100 tests.

と太鼓判を押してくれるものの、どこまで信用していいのか分かりません。
QuickCheckの開発者の一人であるJohn Hughesも、

" The biggest danger in using QuickCheck is that we no longer see the test data."
(Hughes, 2009) pdf

と言っています。
そこで、QuickCheckテストの信頼度をチェックする方法を調べてみました。

QuickCheckテストの信頼度チェック:方法

コードカバレッジを見る

生成されたテストデータの妥当性を検証する正当な方法は、コードカバレッジを見ることです。
GHCにはデフォルトでhpcというカバレッジ計測ツールが組み込まれています。
このhpcが計測するのはBoolean Coverageと言い、ガードや、if、case等の分岐がそれぞれ実行されたかを表します。C0,C1,C2でいうと、C1に当たると思います。
実際の実行に関してはRWHコード網羅率を観察するに詳しいのでそちらを参照してください。
おおまかには、ghcでは-fhpc、cabalでは--enable-library-coverageオプションをつけてconfigureを行えば実行時にtixファイルを生成します。

分布を見る

分布を見る方法で簡単なものは、QuickCheckに用意されているcollectやclassifyを用
いる方法です(QuickCheck Manual)。

prop_Insert x xs = 
    ordered xs ==> 
        collect (length xs)$
        classify (ordered (x:xs)) "at-head"$
        classify (ordered (xs++[x])) "at-tail"$
        ordered (insert x xs)
  where types = x::Int

上のプロパティを実行すると、

Main> quickCheck prop_Insert
OK, passed 100 tests.
58% 0, at-head, at-tail.
22% 1, at-tail.
13% 2.
4% 1, at-head.
3% 3.

このように分布が出力されます。

ここで、意図しないデータ生成が多いようであれば、プロパティに対して付けた制約が
悪いか、自ら定義したデータ型であれば、arbitraryの記述が悪いと気づくことが出来
ます。

cabal testで使いたい

cabalからの実行は以下のオプションをセットします。

cabal configure --enable-tests --enable-library-coverage && cabal build && cabal test

これでカバレッジ情報が記述されたtixファイルが生成されるはず。
残念ながら、私は実行したところtixファイルは生成されましたがカバレッジ情報が出ませんでした。
私はcabal-dev0.9.1を使っているのですが、@shelarcyさんの記事こちらのチケットを見るとcabal最新版では修正されているようです。
また、現状ではHspecがcabalからの実行だと標準出力結果をログに残さないようなので分布ログは残らず。
あと、カバレッジが規定値を下回った際、testをfailにするか警告を出せるといいかもしれません。

まとめ

継続的開発のためにテストの質を維持したいという動機がありました。
そこで、QuickCheckテストが該当コードを正しくテストできているのか、コードカバレッジとテストデータの分布を見ることで検証する方法を紹介しました。
また、上記チェックを自動テストに載せるためにcabalでのオプション設定を模索しました(すいません、ここは道半ばです)。
検証用に用いたプロジェクトは以下です。各種設定の参考にしてください。
github

以下、雑感

  • QuickCheckは、実行ごとに同じ値を生成するわけではないので、厳密にはリグレッションテストにはふさわしくないかもしれません。ただ、カバレッジと合わせての運用であれば使えそうな気もします。
  • QuickCheckでは、値をランダムに生成します。なので、分岐のコーナーを全て突く”可能性”があります。ただそれは可能性なので、カバレッジ見てコーナーを突いているか監視する必要があると思います。
  • ほとんどRWHの焼き直しですいません(;^ω^)
  • QuickCheckは小規模でまとまったコードなので初心者のコードリーディングに最適です。依存モジュールがbaseとrandomだけなので、dep-hellに悩まされずに済む!!涙

では皆さん、メリークリスマス!!

(追記)

  • 論文pdfへのリンクを追加
  • QuickCheckでプロパティをチェックしつつ、HaskellコードをAgdaに変換して正しさを証明しようというアプローチもあるようです。
    • Verifying Haskell Programs Using Constructive Type Theory pdf
    • HakellWikiにあるTesting & Correctnessの論文集 link

Real World Haskell―実戦で学ぶ関数型言語プログラミング

Real World Haskell―実戦で学ぶ関数型言語プログラミング


すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!


プログラミングHaskell

プログラミングHaskell