a wandering wolf

Does a wandering wolf dreams of a wondering, sometimes programming sheep?

このエントリーをはてなブックマークに追加

Persimmon.Dried で性質をチェックする

性質をチェックする

プログラムをテストするときに、アサーションで特定の値を使ってテストを書いていると、「このプログラムがある性質を満たしていることを確かめたいな」という思いを上手く表現できないことがあります。それは、「ある性質を満たす」というのを広範囲の入力に対して確かめたくなるからだったりします。(※当社調べ)

2、3の入力に対してであれば、直接そういうテストを書いたり、パラメタライズされたテストを使って書いたりできます。しかし、「x > 0 である int に対して」とかだとどうでしょう。同値分割とかを用いて効率的にテストを書く事はできると思いますが、そもそもそういうことを言いたいんじゃないよね、とか。「x > 0 である任意の int に対してテストしたい」はずなのです。

そういうシチュエーションに対して、いわゆる QuickCheck が効果を発揮します。(あえて「証明」と言わないところは察してください)

干し柿という選択肢

いろんなプログラミング言語に QuickCheck の実装が存在します。関数型プログラミング言語を標榜しているもので特によく見かける気がします。本家本元の Haskell、Scala には ScalaCheck、F# にも FsCheck というのがあります。

一方、F# にはここでよくご紹介している Persimmon というテスティング・フレームワークがあります。この Persimmon のラインナップに Persimmon.Dried というものがあり、QuickCheck 的なテストを書けるようにしてくれます。

干し柿の食べ方

基本的に公式のドキュメント(上記リンク)を読めば、使いはじめることはできます。よくある「リストを反転してさらに反転させたものは、元のリストに戻る」と言う性質は次のように書くことができます:

open Persimmon
open Persimmon.Dried

let ``reverse and reverse is original`` = Prop.forAll (Arb.list Arb.int) (fun xs ->
    List.rev (List.rev xs) = xs
)

ここでは、特定の値を使ってはいません。「int list に対してリストを2回反転すると、元のリストに等しい」ということを表現していますが、これで性質がチェックできます。

テストの実行は F# スクリプトに書いて実行することもできますが、Persimmon.Console を使うのが便利です。

open UseTestNameByReflection

let ``Check property`` = property {
    apply ``reverse and reverse is original``
}

このコードをビルドして、できた exe や dll を Persimmon.Console に食べさせると、先ほどのリストに関する性質をチェックしてくれます。

最高の干し柿へ

なかなか使える Persimmon.Dried ですが、個人的に改善したいポイントがあります。

1つは apply した先でテストに失敗した時に、どの apply で失敗したかが明確ではない点です。大体はどこで失敗したか予想はつきますが、1つの property ブロックに複数の apply を入れていると、失敗結果から自明には分かりません。どの Prop が失敗したか、名前が分かるととても捗りますね。

もう1つはドキュメントです。Getting Started があるので使い出すことはできますが、果たしてこの干し柿でどこまで出来るのかが明らかではありません。Funcy で干し柿を使ってみましたが、Intellisense の助けを借りて頑張った感があります。

ドキュメントについては、Funcy での利用から知見を貯めて還元したいと思っています。マジで。

最後に

Persimmon は便利なテスティング・フレームワークで、これを使ってプロパティ・ベースのテストが書けるというのは、便利さにより磨きがかかります。Persimmon.Dried は上手く使えば簡単に性質をチェックできるテストが書けるので、今後も常用していきたいですね。