maaash.jp

what I create

App::Prove::Plugin::MySQLPool #perl

最初は、テスト用のmysqldを立ち上げっぱなしにしておいて、
それを使うテストを prove -lr t で走らせたものであった。

そのテストに10分以上かかるようになると、我慢がならなくなり、
Test::mysqldを使い、

1
prove -lr -j4 t

で走らせるようになった。

それでも10分以上かかるようになると、また我慢がならなくなり、
App::Prove::Plugin::MySQLPool – github, cpan
というのを書き、

1
prove -lr -PMySQLPool=MyApp::Test::DB -j4 t

でテストを走らせるようになった。

App::Prove::Plugin::MySQLは、Test::mysqldを使ったテストが大量にある時に、
テストの最初に -j* 分のmysqldを起動し、
それぞれの *.t にはそのmysqld群の中から使っていないものを
割り当てるproveプラグインである。

Test::mysqldを使ったテストの最初では、CREATE TABLEをしまくったり、マスターデータをINSERTしまくったりするだろう。

App::Prove::Plugin::MySQLは、-j*分のmysqldを起動すると、それぞれに対して、

1
-PMySQLPool=MyApp::Test::DB

として指定したモジュールの

1
prepare

関数を呼ぶ。
(指定しない場合には何もしない。)

その中で、GitDDLを使って、

# MyApp::Test::DB
sub prepare {
    my ($package, $mysqld) = @_;
    my $gd = GitDDL->new( dsn => $mysqld->dsn, ... );
    $gd->deploy;
}

というようにしてテストの前準備をすればいい。

これが終わると、私のテストをやっと実行する。

1
$ENV{ PERL_TEST_MYSQLPOOL_DSN  }

にdsnがやってくるので
こんなふうにして使う。

my $dbh = DBI->connect( $ENV{ PERL_TEST_MYSQLPOOL_DSN } );
$dbh->do( "TRUNCATE $_" ) for @tables;

... # your test code

同じmysqldが使い回されてくるので、中身を毎度消してやる。

実装には試行錯誤したし、いまいちなところも多い気がする。

テストコードをなるべく変更せずに(%ENVの書き換えだけで)使えるようにするには、POSIX::AtForkを使ってfork後に子プロセス内で%ENVをセットした。
他にproveプラグインでうまくフックできるのだろうか?

1つの *.t ファイルのテストの前にmysqldをpoolから確保し、
テストが終了したら、そこで使ったmysqldは開放して次のテストに割り当てたい。
get_and_setをatomicにできるCache::FastMmapを使って、
確保時にはPOSIX::AtForkで子プロセス内から自分のpidをキャッシュにセットして、
開放時には終了したテストのpidをとれなかったので、死んだプロセスがあるかどうかをpsでまわして調べるなどしている

よくわからないだろうけれど、がんばったし、
テストはまた4分くらいで終わるようになったし、よく眠れそうだ。

Comments