tsucchi’s diary(元はてなダイアリー)

はてなダイアリー(d.hatena.ne.jp/tsucchi1022)から移行したものです

Test::DataLoader::MySQL というモジュールを書いた

意外と早くアップロードされたなぁ。CPAN Author になりました。

Test::DataLoader::MySQL - search.cpan.org

これは何か、というと、テストコード中でテストデータをDBに流し込むためのモジュールです。

最近のお仕事の話とかデータベースのテストの話とかに少し書きましたが、今のウチのプロジェクトはモデルが DB のテーブルそのもので、ビジネスロジックがストアドプロシージャに書いてあったりします。

こういう環境では、モックオブジェクトが使えず、DB に直接データを流し込む以外にテストする方法がありません(多分)。また、安易にモジュールがインストールできない環境のために、依存するモジュールをなるべく少なくたいという(自分の)要望があって、そうしています。(DBI と DBD::mysql が入っていれば動くはず。テストコードを動かすためには Test::mysqld が入っているほうが望ましいですが)。

一応怪しい英語で POD にたくさんドキュメントを書いたけど、わかりにくいと思うので、ここでも解説してみます。

基本的な使い方はこんな感じ。

my $data = Test::DataLoader::MySQL->new($dbh); # DBI からもらった handler を new に渡して。。。
# add します。このタイミングでは、モジュールにデータを教えているだけ。実際にDB には入れません
$data->add('foo', #テーブル名
            1,    #データID
             {# 流し込みたいデータ(data_href: column => value)
                 id => 1, 
                 name => 'aaa',
             },
             ['id']); # 主キー(array_ref)

$data->load('foo', 1); #さっき登録した、テーブル名'foo', データID'1' のデータを DB に流し込みます
# ...
# で、あとは好きなだけテスト書く

データ ID はただのハッシュのキーなので、何でもいいです。私のプロジェクトでは数字を使っていました。

デフォルトでは、流し込んだデータはオブジェクト(ここでの例なら $data)が破棄されるタイミングで DELETE されます。データ削除は、add メソッドの最後の引数の array_ref をキーにして DELETE 文を発行しています。

なので、キーなしのテーブルの場合もテストデータ内で一意になるような擬似的なキーを決めて書くようにしてください。

new にオプションで Keep=>1 と指定すれば、オブジェクトが破棄されてもデータは残ります。

my $data = Test::DataLoader::MySQL->new($dbh, Keep=>1); #こうするとデータが残る

主キーがオートナンバー(AUTO_INCREMENT)の場合は、load メソッドの戻り値を使います。

my $data = Test::DataLoader::MySQL->new($dbh);
$data->add('foo',
            1,
            {
                name => 'aaa',
            },
            ['id']);

my $key = $data->load('foo', 1);
my $id = $key->{id}; #ここでオートナンバーで設定される値(キーのid)が $id に入る

複数のテストで共通のテストデータを使いまわしたい場合は、そのファイルで add します。この場合はコンストラクタ new の代わりに init を呼んでください。たとえば、下記のような内容で、testdata.pm を作ります。

# testdata.pm
my $data = Test::DataLoader::MySQL->init(); #外部ファイルでは new ではなくinit を使う
$data->add('foo',
            1,
            {
                id   => 1,
                name => 'aaa',
            },
            ['id']);
$data->add('foo',
            2,
            {
                id   => 2,
                name => 'bbb',
            },
            ['id']);
# ...こんな感じでデータをたくさん登録

で、テストコード側では、load_file で、さっき登録したデータを読み込みます。

# in your testcode
my $data = Test::DataLoader::MySQL->new($dbh);
$data->load('foo', 1); #使うデータだけロードすればよい
# ...でデータを使うテストを書く

ほとんどのテストコードで使う共通ファイルを外部に置き、あるテストコードに固有のデータはそこで add するほうが多分わかりやすいです。ウチのプロジェクトは単一ファイルに置いて、ハイパーカオスを作り上げてしまいましたが。。。

わからない点があれば、テストコードを見るのが早いと思います。テストコードに書いてある以上の機能は作りこんでいないので。

今回はプロジェクトで使っていたものをベースに、書き直しをしたものではありますが、基本的な考え方やインターフェースは変えていません。コレの元ねたのモジュールを使って、400 ファイルくらいのテストコードを書いているので結構実績もあるはず。

とはいえ、書き直しているので、もしかすると全然ダメダメかもしれません。その場合は RT でもメールでも何でも良いのでご連絡をお願いします。