修正前
- テスト全体で1つのmysqldなので、並列で動かない
- 無駄に頑張りすぎていて、簡単に理解できない
- t::Utilがごちゃごちゃしていてつらい
- apptestの実装がトランザクションを使用していて、テストに影響がありそう
ゴール
- テストの修正はなるべくしたくない。
- 並列でテストを動かせるように。
- 自分で頑張り過ぎない。
- t::Utilにはロジックを書かない。
- apptestは、トランザクションに依存した実装にしない。
半日で出来たので、難しい事はしてないです。
並列でテストを動かす
修正前の状態と問題
mysqldが立ち上がっていたらそれを使い、立ち上がっていなければTest::mysqldを使用して立ち上げる。ブランチ毎にデータベースを作り、スキーマの更新もよしなに行うようになっていた。
テスト全体で1つのmysqldを使うようになっているので、並列で動かず、テストが終わるのがどんどん遅くなる。
修正
複数立ちあげれるようにApp::Prove::Plugin::MySQLPoolを使うように変更。
prove -PMySQLPool=MyApp::Test::DB -j4 -r t
という感じにすれば並列で動くようにした。
MyApp::Test::DBはこんな感じです。
package MyApp::Test::DB; ...; sub prepare { # for App::Prove::Plugin::MySQLPool my ($class, $mysqld) = @_; $class->setup_db($mysqld); } sub setup_db { my ($class, $mysqld) = @_; # ddlを作るのは一度しか実行しない my $file; if (!$ENV{MYAPP_TEST_DDL_FILE}) { $file = File::Spec->catfile($mysqld->my_cnf->{tmpdir}, "schema.sql"); # ddlを作ってくれる君 MyApp::CLI::DB::DDL->new(file => $file)->run; $ENV{MYAPP_TEST_DDL_FILE} = $file; } else { $file = $ENV{MYAPP_TEST_DDL_FILE}; } my $opts = _mysql_dsn_to_options($mysqld->dsn); system("mysql $opts test < $file")==0 or die "Cannot update schema: \n-- $!\n"; }
やらなかった事
テスト開始時に1つだけmysqldを立ち上げて、テストファイル毎に使うDBを変える。
- 昔書いたけど、ちゃんと動くか不安だった。
- 他の人が書いたものに頼ったほうが楽だと思った。
t::Utilにはロジックを書かない
修正前の状態と問題
t::Utilに必要そうな関数を生やしていたけど、どんどん肥大化して読みにくい。
修正
ロジックは、MyApp::Test以下にいれるようにした。
例えば、Shodoでドキュメントのひな形を作るようにしているのでそれの例。
package MyApp::Test::Shodo; sub suzuri { my ($class) = @_; # テストファイルににあるrequestをmockする処理とかが書かれてる ...; } package t::Util; our @EXPORT = qw(suzuri); sub suzuri { MyApp::Test::Shodo->suzuri(@_) }
apptestは、トランザクションに依存した実装にしない。
修正前の状態と問題
http://hisaichi5518.hatenablog.jp/entry/2013/08/19/163916
apptestで囲むとその中でしかデータが保持されないようになっていた。
しかしながら、トランザクションで囲んで最後にロールバックするようにしていたので、それ不安じゃね?と思っていた。
修正
最後にtruncateする
sub apptest { my ($class, $note, $code) = @_; Test::More::subtest($note, $code); # DBのデータ全部削除 MyApp::Test::DB->truncate_all; }
実際は、Redisのデータとかログのデータとか削除したり、色々している。
まとめ
ほんとは、もっとやった事あった気がするけど、書くのがめんどくさくなった。