本稿では preparation.txt を使ってテスト用の MySQL データベース一式を用意する方法を説明します。
[:contents]
* 概要
MySQL データベースを使用するテストでは、予め MySQL のデータベースやテーブル、初期データを準備しておきたいことが非常によくあります。 prepare-db-set.pl を使うと、 preparation.txt に所定の書式で記述した通りのデータベースを簡単に用意することができます。
prepare-db-set.pl は Test::mysqld を使って一時的な MySQL サーバーを起動し、 preparation.txt に従ってテーブルを用意します。用意したデータベースのリストは JSON ファイルとして出力されるので、テストスクリプトはその dsn を DBI などに与えてアクセスさせることができます。
* preparation.txt
preparation.txt は1行に1つ命令を書くことができるテキストファイルです。前の行から後の行へと順に処理されていきます。空行や行の「#」より後 (コメント) は無視されます。 preparation.txt というファイル名がよく使われますが、実際には好きな名前で構いません。
コマンドには次のものがあります。
- db {dbname}
-- 指定された名前のデータベースを作ります。 (実際には指定された名前そのものではなく、接頭辞や接尾辞が必要に応じて付加されます。)
-- JSON ファイルからはここで指定したデータベース名をキーとして dsn を取得できます。
-- 以後のコマンドは指定された名前のデータベースについて実行します。
- use db {dbname}
-- 以後のコマンドは指定された名前のデータベースについて実行します。既に作られたデータベースでなければエラーになります。
- table {filename}
-- 指定されたファイルから CREATE TABLE 文を抜き出して実行します。ファイルが存在しなければエラーになります。
- dbtable {filename}
-- 指定されたファイルから CREATE DATABASE / CREATE TABLE 文を抜き出して実行します。ファイルが存在しなければエラーになります。
- alter table {filename}
-- 指定されたファイルから ALTER TABLE 文を抜き出して実行します。ファイルが存在しなければエラーになります。
- insert {filename}
-- 指定されたファイルから ALTER TABLE 文を抜き出して実行します。ファイルが存在しなければエラーになります。
- import {filename}
-- 指定されたファイルを preparation.txt の一部とみなして実行します。ファイルが存在しなければエラーになります。
- import glob {filename}
-- 指定されたファイルを preparation.txt の一部とみなして実行します。
-- シェルのワイルドカードを解釈します。一致するファイルが複数あっても構いません。ワイルドカードを含む場合、一致するファイルがなくてもエラーになりません。
ファイル名は preparation.txt からの相対パスとして解釈されます。
* prepare-db-set.pl
prepara-db-set.pl は次のようにして使います。
>|sh|
# 起動
$ perl path/to/prepare-db-set.pl \
--preparation-file-name=path/to/preparation.txt \
--dsn-list=path/to/dsns.json
# 終了
$ perl path/to/prepare-db-set.pl \
--stop
--dsn-list=path/to/dsns.json
||<
* dsns.json
作成されたデータベースに接続するための dsn は dsns.json に記録されています。
例えば、
>||
db dbname1
table path/to/dbname1.sql
||<
... というファイルを使って
>||
$ perl path/to/prepare-db-set.pl \
--preparation-file-name=path/to/preparation.txt \
--dsn-list=path/to/dsns.json
||<
... と起動すると、 dsns.json は
>|javascript|
{
"dsns": {
"dbname1": "DBI:mysql:dbname=dbname1_1_test;mysql_socket=/tmp/k3yByCvo9R/tmp/mysql.sock;user=root"
}
}
||<
... といった内容になるので、この dsn を使って作られたデータベースにアクセスできます。
*module* テストの中で preparation.txt からデータベースを用意する
Test::AnyEvent::MySQL::CreateDatabase を使うと、 Perl コード中の任意の時点で preparation.txt を元に MySQL データベースを用意することができます。
>|perl|
use Test::AnyEvent::MySQL::CreateDatabase;
use Path::Class;
$prep_f = file("path/to/preparation.txt");
$cv = Test::AnyEvent::MySQL::CreateDatabase->prep_f_to_cv($prep_f);
$cv->cb(sub {
my $obj = $_[0]->recv;
$obj->context_begin;
my $json_f = $obj->json_f; # Path::Class object
my $json = file2perl $json_f;
my $dsn = $json->{dsns}->{dbname};
# ... Access $dsn
$obj->context_end(sub {
warn "MySQL stopped";
});
});
||<
ここで $obj->context_begin / $obj->context_end は参照カウンターを操作するメソッドで、カウンターが0になると MySQL サーバーを停止します。
これは Test::X1 の要求するインターフェイスなので、 Test::X1 を使っていれば次のように書けます。
>|sql|
my $mysql_cv = Test::AnyEvent::MySQL::CreateDatabase->prep_f_to_cv($prep_f);
test {
my $c = shift;
my $json_f = $obj->json_f; # Path::Class object
my $json = file2perl $json_f;
my $dsn = $json->{dsns}->{dbname};
# ... Access $dsn
done $c;
} wait => $mysql_cv;
test {
...
} wait => $mysql_cv;
...
||<
$obj->context_begin / $obj->context_end は自動的に呼ばれて、 $mysql_cv を使っている最後のテストが終わった時に MySQL サーバーが停止します。