つよく、やさしく、泥臭く生きていくブログ

日常とポエムと、ときどき技術

Test::mysqld で copy_data_fromの機能を使う

雑なブログの方が毎日続いていて偉い。

最近はテストを書こうとしていて、そんな中でTest::mysqldを使ってみて躓いたところを書く。

そもそもTest::mysqld

めっちゃ便利感あって、DBに入れたり消したり更新したりするような処理をテストするときって、テストで使ったデータのお掃除が必要になる。
でもこいつを使ってやると、テストのたびに新しくmysqldを立ち上げ、その中でDBの処理を行い、終わったら綺麗に消えてくれる。

ただし、新しく立ち上がるmysqlは初期状態なので、テストするDBと同じようにテーブル作ったりデータを流し込むSQLを流し込んでやらないといけない。
が、copy_data_from というメソッドがあり、これを使うと、実際のデータから同じ環境を一発で用意してくれる。すごい。

この辺とか見てたわけです。 Test::mysqld 0.17 でテストがもっと簡単になる話 - kazuhoのメモ置き場

今回の要件

MyApp/DB.pmという自作モジュールがあり、DBへの接続とかユーティリティ的メソッドを持っている。こいつをテストしたい。

とりあえずテストコード

留意点はコード中にコメントで書いた

こいつをテストしたい
package MyApp::DB

# $ENV{TEST_DSN}があれば見るようにする。
# こうすることで、テストやデバッグの際に任意のDBへ接続させられる
my $schema;
if ( $ENV{TEST_DSN} ) {
    $schema = MyApp::DB::DBIC::Schema->connect( $ENV{TEST_DSN});
}
else {
    my @dsn = ('dbi:mysql:mydatabase', 'USER', 'PASS');
    $schema = MyApp::DB::DBIC::Schema->connect(@dsn);
}
test.t

use Test::More;
use Test::mysqld;
use DBI;
use MyApp::DB;

$ENV{DBI_PASS} = 'hogehoge'; # 読み込ませるDBに設定されているパスワードを書く

# test 用 mysqld 起動
my $mysqld = Test::mysqld->new(
    my_cnf => {
        'skip-networking' => '',
    },
    base_dir       => 'tmp/test_mysql',
    copy_data_from => '/hoge/foo/mysql',     # 実際のDBのディレクトリをコピーしてきたものでOK。ただし、mysql.sockファイルは削除しておく。
) or plan skip_all => $Test::mysqld::errstr;


# test 用 mysqld 用の dsn を環境変数に設定する
$ENV{TEST_DSN} = $mysqld->dsn(dbname => 'mydatabase');
#  こうしてMyApp::DBをnewすると、MyApp::DBに書いた通り$ENV{TEST_DSN} のdsnにつないでくれる。
my $db = MyApp::DB->new();

print $db->mymethod();

上記で、$dbからMyApp::DBのメソッドをテストしてやると、テスト用mysqlに対して読み書きしてくれる。

躓いたところとコメント

  • コピーしてきたデータの不備
    実際のDBのディレクトリをコピーしてきたものを使おうとすると、「そのようなファイルはありません」的なエラーが出ていた。
    動いているDBのディレクトリからコピッてきたため、mysql.sockファイルがあり、これのせいだった。
    コピーしてきた中のmysql.sockは削除しておく。
  • 環境変数には文字列しかダメ
    元々はdsnには上記の@dsnのように配列の形式でやるようにしていた。
    なので、$ENV{TEST_DSN} に元々と同じように配列を入れてやったり、配列のリファレンスを渡してデリファレンスして使ったりしようとしたがダメだった。
    どうやら、$ENV{hoge}には単純に文字列しか入れられないようだ。ほんまかいな。違ってたら教えてください。
    で、実際のところ、dsnには文字列の形で指定することもでき、
    $mysqld->dsn(dbname => 'mydatabase'); をprintしてみると分かるが、文字列の形で返されている。
    なので問題なし。
  • パスワード
    Test::mysqldはnewするとDBに接続する動作がある。
    初期状態だったら問題ないが、ccopy_data_from によって実際のDBの状態を作ってくれるため、 実際のDBのパスワードが無いと接続に失敗してこける。
    そういう場合のため、$ENV{DBI_PASS} に実際のDBのパスワードを入れておいてあげると、 DBIの動作の中でこいつを使ってくれる。

ちなみに、パスワード直書きが嫌だったので、とりあえずパスワードらしく入力させる方法はこれ。

system("stty -echo");    # 入力を表示しなくなる
print "Enter DB password: ";
my $passwd = <STDIN>;      # 標準入力させる
system("stty echo");      #入力表示を戻す
chomp( $passwd );