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

$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

Test::TCPを使ってテスト用にmemcached, app, nginxサーバを立てる

テストをするときに、テスト用のみのMySQLとかmemcachedとか、アプリケーションサーバとかを立てたい時がある。MySQLの場合はTest::mysqldを利用すればすぐできるが、それ以外の場合もdaemonの場合はTest::TCPを使ってすぐにテスト用のサーバを立てることができるので、そのメモ。

テスト用memcachedサーバを作る

  • Test::TCPが渡してくれるportを使って起動
  • Test::TCPが返すオブジェクトがguardのようなオブジェクトになっていて、スコープが切れるとプロセスを勝手に落としてくれる
my $memd = Test::TCP->new(
    code => sub {
        my $port = shift;
        exec "memcached -p $port";
    },
);

my $client = Cache::Memcached::Fast->new({
    servers => ["localhost:" . $memd->port],
});
$client->add('skey', 'text');
warn $client->get('skey');

テスト用アプリケーションサーバを立てる

適当にPlack Applicationサーバを立てる。HTTP::Server::PSGIを利用する。

my $app_server = Test::TCP->new(
    code => sub {
        my $port = shift;

        my $server = HTTP::Server::PSGI->new(
            host    => "127.0.0.1",
            port    => $port,
        );

        $server->run(sub {
            [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ]
        });
    }
);

my $ua = LWP::UserAgent->new;
my $res = $ua->get('http://localhost:' . $app_server->port);
warn $res->content;

テスト用nginxサーバを立てる

  • Test::TCPが渡すportを使って、先ほど作ったapp_serverにproxyするテスト用nginxサーバを立てる
  • nginxの一時設定ファイルを動的に作成し、それを使って起動
# app serverを立てる
my $app_server = Test::TCP->new(
    code => sub {
        my $port = shift;

        my $server = HTTP::Server::PSGI->new(
            host    => "127.0.0.1",
            port    => $port,
        );

        $server->run(sub {
            [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ]
        });
    }
);
my $app_port = $app_server->port;

# nginxを立てる
my $nginx = Test::TCP->new(
    code => sub {
        my $port = shift;

        my $temp_dir = File::Temp::tempdir;

        # 渡されるportを利用して一時設定を作成
        my $conf = <<"EOS";
daemon off;

error_log $temp_dir/error_log crit;
lock_file $temp_dir/lock_file;
pid $temp_dir/nginx.pid;

events {
    worker_connections  1024;
}

http {
    client_body_temp_path $temp_dir/client_body_temp;
    proxy_temp_path $temp_dir/proxy_temp;

    upstream app {
        server 127.0.0.1:$app_port;
    }

    server {
        listen $port;
        location / {
            proxy_pass http://app;
        }
    }
}
EOS

        my $fh = Path::Class::file("$temp_dir/nginx.conf")->openw;
        print { $fh } $conf;
        close $fh;

        # 起動
        exec "nginx -c $temp_dir/nginx.conf -p $temp_dir";
    },
);

my $ua = LWP::UserAgent->new;
my $res = $ua->get('http://localhost:' . $nginx->port);
warn $res->content;

まとめ

こういう感じでportを指定してdaemonを立てられればTest::TCPを使って一時的にprocessを立ち上げることが簡単にできる。便利。

次回はこういう感じなのを使ってNginxの設定をテストするというのを紹介しようと思う。