Perlとテスト

テストというものをほとんど知らなくてやばいので、勉強します。モダンPerl入門 (CodeZine BOOKS)によるとCPANの形式で開発を進めていくとテストを走らせるための基本機能も組み込まれているということなので、CPAN形式にまとめます*1。こんな感じ。

/Users/syou6162/perl/Example-Software% ls -R
Makefile.PL lib         t

./lib:
Example

./lib/Example:
Software.pm

./t:
00_compile.t

雛形を作るためのモジュールもあるようだ。

この後、各ファイルを書いたら(テストもPerlで書ける!)

perl Makefile.PL
make
make test

を走らせればよい。新規テストを作成したらmake cleanする必要があるらしい。

テストの数を数えるのが面倒なとき

no_planでおk。

use Test::More q/no_plan/; # いくつか数えるのが面倒なので

実際にテストをやってみる

実際にやってみるよ!!深さ優先探索のやつを対象にするよ。微妙にエラー処理が加わっている。

package Graph::DFS;
use strict;
use warnings;
use Perl6::Say;
use base qw(Class::Accessor);

Graph::DFS->mk_accessors(qw(edges nodes));

sub new {
    my $class = shift;
    my $self = {
		edges => [],
		nodes => {}
	       };
    bless ($self, $class);
    return $self;
}

sub add_edge {
    my ($self, $edge) = @_;
    push @{$self->{edges}}, $edge unless grep{$_ eq $edge} @{$self->{edges}};
}

sub add_node {
    my ($self, $u, $v) = @_;
    $self->add_edge($u);
    $self->add_edge($v);
    push @{$self->{nodes}->{$u}}, $v unless grep{$_ eq $v} @{$self->{nodes}->{$u}};
}

sub dfs {
    my $self = shift;
    my $start = shift;
    my $has_approached = shift || [];
    my $indent = shift || 0;
    unless(grep {$start eq $_} @{$self->{edges}}){
	die "$start was not included in edges of graph.\n";
    }
    say " " x $indent, $start;
    push @$has_approached, $start;
    foreach my $node (@{$self->{nodes}->{$start}}) {
	unless(grep{$_ eq $node} @$has_approached) {
	    $self->dfs($node, $has_approached, $indent + 1);
	}
    }
}

1;

テストをするためのコード

テストをするためにも色々な関数が用意されているということが分かった。

use strict;

use Test::More q/no_plan/; # いくつか数えるのが面倒なので
use Test::Exception;

use_ok("Graph::DFS");

my $g = Graph::DFS->new();

ok(defined $g, "$gはundefではない");
isa_ok($g, "Graph::DFS");

$g->add_edge("1");

# メソッドの確認
can_ok($g, "new");
can_ok($g, "add_edge");
can_ok($g, "add_node");
can_ok($g, "dfs");
can_ok($g, $_) for qw(new add_edge add_node dfs); # これでもいい

# add_edgeとadd_nodeの挙動の確認
is(@{$g->{edges}}[0], "1", '@{$g->{edges}}[0]は1を返す');
isnt(@{$g->{edges}}[0], "2", '@{$g->{edges}}[0]は2を返さないことを確認');
is_deeply($g->{edges}, [1], "edgesの要素を確認");
$g->add_edge("1");
is_deeply($g->{edges}, [1], "edgesの要素が重複していないことを確認");
$g->add_edge("2");
is_deeply($g->{edges}, [1, 2], "edgesの要素を確認");
$g->add_edge("3");
$g->add_edge("8");
$g->add_node("3", "2");
is_deeply($g->{nodes}, {
			3 => [2]
		       }, "nodesの要素を確認");
$g->add_node("1", "5");
is_deeply($g->{nodes}, {
			1 => [5],
			3 => [2]
		       }, "nodesの要素を確認");
$g->add_node("1", "6");
is_deeply($g->{nodes}, {
			1 => [5, 6],
			3 => [2]
		       }, "nodesの要素を確認");
$g->add_node("2", "6");
is_deeply($g->{nodes}, {
			1 => [5, 6],
			2 => [6],
			3 => [2]
		       }, "nodesの要素を確認");
$g->add_node("1", "3");
is_deeply($g->{nodes}, {
			1 => [5, 6, 3],
			2 => [6],
			3 => [2]
		       }, "nodesの要素を確認");
$g->add_node("2", "9");
is_deeply($g->{nodes}, {
			1 => [5, 6, 3],
			2 => [6, 9],
			3 => [2]
		       }, "nodesの要素を確認");

# dfsの確認
ok(defined $g->dfs("1"),"DFSが成功するはず");
dies_ok{ $g->dfs("10000") } "例外の発生に成功";

実行結果

青信号になった!!!

/Users/syou6162/perl/Graph-DFS% prove -vl t
t/00_compile.t .. 
ok 1 - use Graph::DFS;
ok 2 - Graph::DFS=HASH(0x818cfc)はundefではない
ok 3 - The object isa Graph::DFS
ok 4 - Graph::DFS->can('new')
ok 5 - Graph::DFS->can('add_edge')
ok 6 - Graph::DFS->can('add_node')
ok 7 - Graph::DFS->can('dfs')
ok 8 - Graph::DFS->can('new')
ok 9 - Graph::DFS->can('add_edge')
ok 10 - Graph::DFS->can('add_node')
ok 11 - Graph::DFS->can('dfs')
ok 12 - @{$g->{edges}}[0]は1を返す
ok 13 - @{$g->{edges}}[0]は2を返さないことを確認
ok 14 - edgesの要素を確認
ok 15 - edgesの要素が重複していないことを確認
ok 16 - edgesの要素を確認
ok 17 - nodesの要素を確認
ok 18 - nodesの要素を確認
ok 19 - nodesの要素を確認
ok 20 - nodesの要素を確認
ok 21 - nodesの要素を確認
ok 22 - nodesの要素を確認
1
 5
 6
 3
  2
   9
ok 23 - DFSが成功するはず
ok 24 - 例外の発生に成功
1..24
ok
All tests successful.
Files=1, Tests=24,  0 wallclock secs ( 0.02 usr  0.01 sys +  0.04 cusr  0.01 csys =  0.08 CPU)
Result: PASS

Test::Class

インストールこけたorz。。。ということでforce install。

*1:なぜかここで読める件について