Perlでのモジュールテストについて

なんか(ダラダラと)長くなるんでさわりだけ先に書く。

$ prove -lr
    • -

Test::SimpleとかTest::Moreとかを使ってテストを書いて実行する。h2xsとかModule::Starterとかは使わない。make testも使わない。
例えばQueueというクラスがあるとする。

package Queue;

use strict;
use warnings;

sub new {
    my ( $class, @items ) = @_; 
    return bless ?@items, $class; 
}

sub size {
    my $self = shift;
    return scalar @$self; 
}

sub enqueue {
    my ( $self, $item ) = @_; 
    push @$self, $item;
}

sub dequeue {
    my $self = shift;
    return shift @$self; 
}

1;

で、こんなテストを書く。

#!perl

use strict;
use warnings;

use Test::More tests => 11;
    
use Queue; 

#################################################
# test size()
    
my $q1 = Queue->new();
isa_ok( $q1, 'Queue' );
is( $q1->size, 0, 'a empty queue' );

my $q2 = Queue->new( qw( foo bar ) );
isa_ok( $q2, 'Queue' );
is( $q2->size, 2, ' a queue with some elements' );

#################################################
# test enqueue()
    
for my $item ( qw( foo bar ) ) {
    $q1->enqueue($item);
}
is( $q1->size, 2, 'a queue is enqueued' );

#################################################
# test dequeue()

my $q3 = Queue->new(); 
isa_ok( $q3, 'Queue' );

is( $q3->dequeue, undef, 'a queue has no elements' );

for my $item ( qw( foo bar ) ) {
    $q3->enqueue($item); 
}

is( $q3->size, 2, 'a queue with some elements' );
is( $q3->dequeue, 'foo', 'first item' );
is( $q3->dequeue, 'bar', 'second item' );
is( $q3->size, 0, 'a queue with no elements' ); 

ディレクトリ階層がこんな感じだったとする。カレントディレクトリはQueueとする。

Queue
  - lib
     - Queue.pm
  - t
     - queue.t

テストを実行するための一番簡単な方法はこうやることだろう。

$ perl t/queue.t

でもこれはうまくいかない。
Can't locate Queue.pm in @INCだって。じゃあqueue.tでuse Queue;する前にuse lib qw(/path/to/lib);すればいいじゃんと思うんだけど、そうすると相対パスなら相対パスの、絶対パスなら絶対パスの欠点がある。実はもっといい方法がある。
こうする。

$ perl -Ilib t/queue.t
1..11
ok 1 - The object isa Queue
ok 2 - a empty queue
ok 3 - The object isa Queue
ok 4 -  a queue with some elements
ok 5 - a queue is enqueued
ok 6 - The object isa Queue
ok 7 - a queue has no elements
ok 8 - a queue with some elements
ok 9 - first item
ok 10 - second item
ok 11 - a queue with no elements 

テストを実行するためのもう1つの方法はTest::Harnessを使うことだ。このモジュールはテストを実行してその結果を分析(?)してくれる。
Test::Harnessをuseしてスクリプトを書いてもいいんだけどめんどくさいのでproveを使う。これはTest::Harnessのラッパーだ。さっきの-Ilibと同じことをするためには-lが使える(-Ilibも使える)。

$ prove -l t/queue.t
t/queue....ok                                                                
All tests successful.
Files=1, Tests=11,  0 wallclock secs ( 0.05 cusr +  0.02 csys =  0.07 CPU)

テストが失敗すると実効値と期待値を表示してくれる。

t/queue....
#   Failed test 'first item'
#   in t/queue.t at line 43.
#          got: 'foo'
#     expected: 'goo'
# Looks like you failed 1 test of 11.
t/queue....dubious                                                           
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 9
        Failed 1/11 tests, 90.91% okay
Failed Test Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
t/queue.t      1   256    11    1   9.09%  9
Failed 1/1 test scripts, 0.00% okay. 1/11 subtests failed, 90.91% okay. 

もしテストファイルが増えたら再帰的に実行すればいい。

$ prove -lr 
t/queue..........ok                                                          
t/queue_class....ok                                                          
All tests successful.
Files=2, Tests=17,  1 wallclock secs ( 0.15 cusr +  0.05 csys =  0.20 CPU) 

感動。
あと、テストを書くのにTest::Classというのもあるんだけど、やることはここに書いたこととあまり変わらないんで割愛する。

    • -

http://svn.takatoshi.dyndns.org/public/examples/PerlTesting/Queue/
Test::More - yet another framework for writing test scripts - metacpan.org
prove - Run tests through a TAP harness. - metacpan.org

    • -

Perl Testing: A Developer's Notebook (Developers Notebook)

Perl Testing: A Developer's Notebook (Developers Notebook)