Plack::Test で HTTP クライアントのテストを書く方法

先日、WWW::Curl をラッピングした HTTP クライアントなモジュールを書いたのですが、テスト用に HTTP サーバーを用意しないといけないと思いつつ、他の HTTP クライアントがどのようにテストをしているか調べてみたところ、WWW::Curl は、

my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";

などとしていて、LWP は Net::HTTPServer を使っているようでした。

ネットワークが繋がっていないとテストが出来ないのもなんですし、Net::HTTPServer 使ってサーバー書くのも面倒臭そうなので、本来の用途とは違いますが、シンプルに書けそうな Plack::Test で書いてみることにしました。

use strict;
use warnings;
use Test::More;
use Plack::Test;

my $app = sub {
    my $env = shift;
    my $content; # something
    return [ 200, [ 'Content-Type' => 'text/plain' ], [ $content ] ];
};

my $client = sub {
    my $cb = shift;

    # ダミーのレスポンスオブジェクトから Plack が起動している URI を得る
    my $req = HTTP::Request->new(GET => '');
    my $res = $cb->($req);
    my $uri = $res->request->uri;

    is( MyHTTPClient->get("$uri/foo/bar"), 'something' );
    ...
};

{
    local $Plack::Test::Impl = 'Server';
    test_psgi $app, $client;
}

done_testing;

ポイントは $Plack::Test::Impl に Server を指定すると、Test::TCP で空きポートを見つけてサーバー起動しますが、ポート番号を $cb からは得られないので、一旦ダミーのリクエストオブジェクトを作ってリクエストを飛ばしてレスポンスオブジェクトから URI オブジェクトを取得しています。こうするとポート番号も得ることが出来ます。

PSGI だとレスポンスの内容を変えるのも楽なのでテストが書き易かったです。

[追記]
id:tokuhirom 氏が、添削で Plack::Loader を使って直接 Test::TCP を使う方法を紹介してくださいました。ありがとうございます!
http://d.hatena.ne.jp/tokuhirom/20100819/1282246989