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

北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

QAハッカソンでTest::BangAwayってモジュール書いた

Perl QA Hackathon 2013 Satellite at Tokyo なるものに参加してきた。こういうイベントで貢献できるガラではないのだけど、開催場所が自分の職場だったのでお邪魔させてもらった。

MillaとMinilla周りの話題が熱い中、前々から作ってみたかったQuickCheckもどきを作ってみた。実はPerlの実装としては既にTest::LectroTestなるものがある。

use Test::LectroTest;
sub mylength { ... }
Property {
    ##[
        x <- String( charset=>"A-Z0-9", length=>[3,] ),
        y <- String( charset=>"A-Z0-9", length=>[3,] ),
    ]##

    mylength($x)+ mylength($y) == mylength ($x . $y);
}, name => "length";

ただ、こいつはソースフィルタ使ってたりメソッド名がキャメルケースだったりとちょっと頂けない部分もあったので、クローンを作った。Test::BangAwayで書くとこんな感じ。

use Test::BangAway;
use Test::BangAway::Generator;

sub mylength { ... }

bang_away_ok {
    my ($x, $y) = @_;
    mylength($x) + mylength($y) == mylength($x . $y);
} concat(string, string);

concat(string, string) という記述で、このテストケースには文字列を2つ渡すよってことを定義している*1。bang_away_ok には、作ったモジュールが満たす性質を書く。ここでは、2つの文字列を1つずつlengthをとって足すのと、結合して1つの文字列にしてからlengthをとるのとは同じになるよ、という性質を定義している。後はbang_away_okが勝手にランダムに引数を生成しまくって、乱れ打ちでこの性質を満たすか調べてくれる。

Generatorは真面目にモナドとして実装してる*2ので、正規分布に沿う値を返すようなのを作ったりしてプリミティブを充実させれば色々できそう。っていうか、本来は一様分布ではなく小さい数値は出やすくて、大きい数値ほど出にくいような分布にしないと効率が悪い。Coarbitraryについてはまだ考えてないので、関数値の自動生成はできない。モジュール名は適当にアルクで検索しただけなのでまだfixしてない。

後、終わった後にDBIx::DataFactoryってのもあるよーと教えてもらった。毛色はちょっと違うけど方向としてはそんな感じ。

*1:静的に型の決まるHaskellでは外付けでこんな定義をしなくてもコンパイラが文字列を2つとることを知っているので、QuickCheckがよろしくやってくれる。実行時に型が決まるPerlではそうはいかないのでテストの定義者が教える必要がある。

*2:LectroTestもそうだけど。