SEEDS Creator's Blog

CGIプログラムをPlack::Testでテストする

以下のようなCGIプログラムのテストを書きたいとします。

  fizzbuzz.cgi

#!/usr/bin/perl
use strict;
use warnings;
use CGI;

my $q = CGI->new;

my $number = $q->param('number');

unless (defined $number) {
    print $q->header(
        -status => 200,
        -type => 'text/plain; charset=utf8',
    );
    print "Please set number";
    exit;
}

my $result = '';
if ($number % 3 == 0) {
    $result .= 'Fizz';
}
if ($number % 5 == 0) {
    $result .= 'Buzz';
}
$result ||= $number;

print $q->header(
    -status => 200,
    -type => 'text/plain; charset=utf8',
);

print $result;

ロジックが一緒に書かれているので、Test::Moreでテストしづらいですね。 こんなときはCGIプログラムをPSGIアプリに変換し、 Plack::Testでテストすると楽です。

 

PSGIアプリに変換するにはPlack::App::CGIBinを使って、 以下の内容のapp.psgiを作成します。

#!/usr/bin/perl                                                                                                                                            
use strict;
use warnings;
use File::Basename;

use Plack::Builder;
use Plack::App::CGIBin;

my $basedir = dirname(__FILE__);

builder {
    mount '/' =>
          Plack::App::CGIBin->new( root => $basedir, exec_cb => sub { 1 } )->to_app;
};

これでめでたくCGIプログラムはPSGIアプリになりました。 普通のPSGIアプリと同様に、plackupコマンドなどで実行できるようになります。

$ plackup app.psgi

ブラウザなどで「http://localhost:5000/fizzbuzz.cgi?number=1」にアクセスすると実行できます。  

PSGIアプリは、Plack::Testでテストすることができます。 先ほどのCGIプログラムのテストは、次のように書くことができます。

use strict;
use warnings;
use Test::More;
use Plack::Test;use HTTP::Request::Common;
use Plack::Loader;
use Plack::Util ();

my $app = Plack::Util::load_psgi('app.psgi');
test_psgi $app, sub {
    my $cb = shift;

    my $res;
    $res = $cb->(GET "/fizzbuzz.cgi?number=1");
    is $res->code, '200';    
    is $res->content, '1';

    $res = $cb->(GET "/fizzbuzz.cgi?number=2");
    is $res->code, '200';
    is $res->content, '2';

    $res = $cb->(GET "/fizzbuzz.cgi?number=3");
    is $res->code, '200';
    is $res->content, 'Fizz';

    $res = $cb->(GET "/fizzbuzz.cgi?number=4");
    is $res->code, '200';
    is $res->content, '4';

    $res = $cb->(GET "/fizzbuzz.cgi?number=5");
    is $res->code, '200';
    is $res->content, 'Buzz';

    $res = $cb->(GET "/fizzbuzz.cgi?number=15");
    is $res->code, '200';
    is $res->content, 'FizzBuzz';
};

done_testing();

書きやすいですね。