Haskellで、手軽にプロパティベーステスト
はじめに
Haskell Advent Calender 2023の2日めの記事です。カレンダーの枠が空いていたので、久しぶりに勢いで書いてみました。
さて、ラムダノート社から「実践プロパティベーステスト」が出版されたので、巷ではプロパティベーステストが流行っていることでしょう (紹介宣伝です)。
Haskellでは、プロパティベーステスト用のライブラリとして、例えばQuickCheckがあります。
この記事では、Haskellではソースコードのコメント中に簡単な記述をするだけで、プロパティベーステストを手軽に実行できることを改めて紹介します。
本格的なコードを開発している局面だけではなく、ちょっと頭の中を整理したい時などにも手軽で便利です。
(前準備) テストケースと期待値によるテスト
Haskellでは、ソースコードのコメント中に埋め込まれたテストケースを実行できるdoctestという便利なツールがあります。
例えば、以下のソースコードがあるとしましょう。
add2 :: Int -> Int -> Int
add2 x y = x + y
上記のソースコードに対して、テストケースとその期待値を、以下の様にコメント部にdoctest用の書式で記載できます。
(下記1行目の-- |
がテストを含む書式部分の開始を意味します。 -- >>>
の行に実行したいテストケースを記述します。 その次の行に期待値を記述します。[1])
-- |
-- >>> add2 10 20
-- 30
add2 :: Int -> Int -> Int
add2 x y = x + y
このファイルに対して次のようにdoctest
コマンドを実行すると、テストケース(add2 10 20
)の実行および期待値(30
)との比較が行われ、その成功や失敗結果が報告されます。
$ doctest prog1.hs
Examples: 1 Tried: 1 Errors: 0 Failures: 0
(本題) プロパティベーステスト
さてdoctest
では、「テストケースと期待値」の組み合わせを自分で明記する方式のテストだけでなく、「満たすべき仕様」であるプロパティを記述するプロパティベース方式のテストも行えます。
doctestは内部的に、QuickCheckライブラリを用いてプロパティベーステストを実行します。
プロパティベーステスト用の記述は単純であり、-- prop>
の行にQuickCheckの記法に従ってプロパティを記載するだけです。
例えば以下はプロパティとして、add2 x y == add2 y x
を記述しています。(add2
関数の2つの入力の順番を入れ替えても結果は常に等しい、ということを表すプロパティです。)
-- |
-- prop> add2 x y == add2 y x
add2 :: Int -> Int -> Int
add2 x y = x + y
プロパティベーステストの実行は、先ほどと同様に以下のようにdoctestコマンドを実行するだけで行えます。
$ doctest prog2.hs
Examples: 1 Tried: 1 Errors: 0 Failures: 0
doctestを実行すると、prop>
で指定された各々のプロパティごとに、ランダムな入力値を生成して多数回のテストを実行し、プロパティが常に成立するか否かを自動的に判定してくれます。上記の例では、Int型のxとyの具体的な値がランダムに生成され、それを元にプロパティが成立するかを多数回に渡ってチェックされます。便利ですね!
まとめ
Haskellでは、ソースコードのコメント中にプロパティを記述するだけで、手軽にプロパティベーステストを行えます。
doctest
コマンドさえあれば、ライブラリの参照や環境のセットアップやテストドライバの記述などが殆ど不要で、手軽にプロパティベーステストを始められます。
プロパティベーステストでは、多数のテストケースを明示的に記述する必要が無いため、比較的に網羅性の高いテストを効率良く行えます。
参考情報など
HaskellのdoctestやQuickCheckについては、古くからいろいろな記事があります。
wadoさんが書かれたまとまった良い解説記事があったのですが、現在はリンクが切れてしまいました。
以下に、その他の昔の参考記事などを挙げておきます。
-
Haskellの単体テスト最前線
-
QuickCheckでデータ駆動型テストを行う
-
QuickCheckで競プロ用Haskellコードをデバッグする
また、書籍「関数プログラミングの楽しみ」の第2章や、「Real World Haskell」の第11にも、QuickCheckの解説があります。
なお、doctestやQuickCheckの公式情報は以下にあります。
-
doctest
-
QuickCheck
あと、QuickCheck用の設定をソースコード中で詳細に行う例は、以下も参照してください。
また、本記事で紹介したdoctestで実行可能なシンプルなコードを以下に置いておきました。
Discussion