読者です 読者をやめる 読者になる 読者になる

パルカワ2

PerlとRubyとイチャラブ

最近やったサーバサイドのテストまわりの修正

修正前

  • テスト全体で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のデータとかログのデータとか削除したり、色々している。

まとめ

ほんとは、もっとやった事あった気がするけど、書くのがめんどくさくなった。