HTTP::Request::AsCGI で CGI をテストする

CGI単体テストは面倒です。Perlお決まりの Test::More と prove でテストしたいし、Webサーバを立ち上げるのかという問題もある。

どうやらこういう場合の定番は HTTP::Request::AsCGI のようなので、試してみた。

こんな感じ。

use strict;
use Test::More;
use HTTP::Request::Common;
use HTTP::Request::AsCGI;
use CGI;

BEGIN {

    #   Create new HTTP::Request::AsCGI instance and setup

    my $c = HTTP::Request::AsCGI->new(
        GET 'http://example.com/path?key1=val1&key2=val2-1&key2=val2-2'
    )->setup;

    #   Test environment variables

    is($ENV{REQUEST_METHOD},    'GET',      '$REQUEST_METHOD');
    is($ENV{HTTPS},             'OFF',      '$HTTPS');
    is($ENV{HTTP_HOST},   'example.com:80', '$HTTP_HOST');
    is($ENV{SERVER_NAME}, 'example.com',    '$SERVER_NAME');
    is($ENV{SERVER_PORT},       80,         '$SERVER_PORT');
    is($ENV{REQUEST_URI},  '/path?key1=val1&key2=val2-1&key2=val2-2',
                                            '$REQUEST_URI');
    is($ENV{QUERY_STRING}, 'key1=val1&key2=val2-1&key2=val2-2',
                                            '$QUERY_STRING');
    is($ENV{PATH_INFO},         '/path',    '$PATH_INFO');
    is($ENV{SCRIPT_NAME},       '/',        '$SCRIPT_NAME');

    #   Test CGI::param

    my $q = new CGI;
    my @param = $q->param;
    my @value = $q->param('key2');

    ok(eq_array(\@param, ['key1', 'key2']), '$q->param');
    is($q->param('key1'),     'val1',       '$q->param(\'key1\')');
    ok(eq_array(\@value, ['val2-1', 'val2-2']),
                                            '$q->param(\'key2\')');

    #   Test Response code, header and content

    $q->charset('utf-8');
    print $q->header('text/plain');
    print 'Hello, world.';

    my $r = $c->restore->response;
    is($r->code,        200,                'Status:');
    is($r->header('Content-Type'), 'text/plain; charset=utf-8',
                                            'Content-Type:');
    is($r->content,     'Hello, world.',    'CONTENT');
}

done_testing;

HTTP::Request::AsCGI のコンストラクタの入力パラメータは HTTP::Request オブジェクトです。HTTP::Request::Common を使えば簡単にセットアップできる。

で、生成した HTTP::Request::AsCGI のインスタンスに対して setup を呼出せば、試験準備完了。上のコードにあるように、環境変数もちゃんとセットされるし、CGI.pm のメソッドも動きます。

出力のテストをする場合は、$c->restore->response で HTTP::Response オブジェクトに復元して各種メソッドでチェックすればよろしい。

ただし、上のコードにあるようにちょっとクセがあって、

  • http: のリクエストのとき、$ENV{HTTPS} にはなぜか 'OFF' が設定される(普通は undef)
  • ポート番号を指定しないリクエストでも $ENV{HTTP_HOST} に ':80' がつく(普通ブラウザはポートを指定しない)
  • $ENV{SCRIPT_NAME} は '/' 固定。なので $ENV{PATH_INFO} もそれにしたがう

となりますので、場合によってはアプリ側に細工が必要になると思います。

まあ、多少クセはあってもこんな便利なものはないと思うので、CGI単体テストに使ってます。