モバイルファクトリーの伏原です。 私は今回Test::Baseというモジュールを紹介させてもらいます。
Test::Baseとは
Test::Baseは、
今回使うサンプルプログラム
NabeAtzz問題を解くプログラムを作ります。NabeAtzz問題にはいくつかのバリエーションがありますが、
- 1からnまでの整数に対して
- 3の倍数の時は
“fool” と出力する - 5の倍数の時は
“dog” と出力する - いずれかの桁に3が含まれる整数の時も
“fool” と出力する - それ以外の数の場合は整数を
“[1]” のように[]で囲って出力する
これを満たすために、
#!/usr/bin/perl -Ilib
use strict;
use warnings;
use NabeAtzz;
for my $n (1..$ARGV[0]) {
print NabeAtzz->shout($n) . "\n";
}
ファイル作成後、
./nabeatzz.pl 20
のように実行すると、
Test::Baseを使ったテストの書き方
さて、
use strict; use warnings; use Test::Base; use NabeAtzz; plan tests => 3; is(NabeAtzz->shout(1), '[1]' ); is(NabeAtzz->shout(4), '[4]' ); is(NabeAtzz->shout(19), '[19]');
まだNabeAtzz.
package NabeAtzz;
use strict;
use warnings;
sub shout {
my ($class, $number) = @_;
return "[$number]";
}
1;
改めて以下のようにテストを実行すると、
> perl -Ilib t/nabeatzz.1.t 1..3 ok 1 ok 2 ok 3
Test::Baseを使ったテストのスタイル
nabeatzz.
use strict;
use warnings;
use Test::Base;
use NabeAtzz;
plan tests => 1*blocks;
filters {
input => [qw/chomp/],
expected => [qw/chomp/],
};
run {
my $block = shift;
is(NabeAtzz->shout($block->input), $block->expected);
};
__END__
=== test 1
--- input
1
--- expected
[1]
=== test 4
--- input
4
--- expected
[4]
=== test 19
--- input
19
--- expected
[19]
スクリプトの行数自体は長くなってしまいましたが、
Test::Baseの記述方法
データ
Test::Baseでは、
=== テスト名(省略可能)
という行で区切られていて、
--- フィールド名 値
という形で入力していきます。ここは、
--- フィールド名: 値
という書き方もできますが、
--- フィールド1: 値 --- フィールド2: 値 --- フィールド3: 値
のようになって行が詰まって見辛くなることがあるので、
テストプラン
普通のテストの場合、
plan tests => 10;
などと記述します。しかし、
use Test::Base; plan tests => 1 * blocks;
blocksは、
plan tests => 1 * 3
と同義ということになります。
フィルタ
入力値、
filters {
input => [qw/chomp/],
expected => [qw/chomp/],
};
filters { に続けて
filters({
input => [qw/chomp/],
expected => [qw/chomp/],
});
このように、
- yaml
- フィールドデータとして複雑な値
(ネストしたハッシュなど) を渡す場合、 YAML形式でデータを記述し、 このフィルタを通すとperlのデータに変換されます - eval
- フィールドデータをperlスクリプトとみなして実行し、
結果を値とします - trim
- データ前後の空白をカットします
他にも、
run
run { ... } の中で実際のテストの処理を記述することになります。この部分もfiltersの時と同じく実質はrun関数を呼び出しているので、
=== テスト名
で記述したテスト名
run {
my $block = shift;
is(NabeAtzz->shout($block->input), $block->expected);
};
フィルタの独自定義とrunの簡略化
フィルタは、
sub nabeatzz { NabeAtzz->shout(shift) }
このように書くと、
sub nabeatzz { NabeAtzz->shout(shift) }
filters {
input => [qw/chomp nabeatzz/],
expected => [qw/chomp/],
};
run {
my $block = shift;
is $block->input, $block->expected;
};
変更したテストも、
perl -Ilib t/nabeatzz.3.t 1..3 ok 1 ok 2 ok 3
実は、
run_is input => 'expected';
run_
run_is
と単純に書けます。
筆者の場合、
run_is_deeply
基本的にはrun, run_
run_is_deeply; __END__ === --- input 1 --- expected data: foo: bar
テストケースの追加
ここまでしっかりとロジック側を設定すれば、
=== test 10 ( multiple of five --- input 10 --- expected dog === test 20 ( multiple of five --- input 20 --- expected dog
プログラムを変更せずにテストを実施すると、
> perl -Ilib t/nabeatzz.4.t 1..5 ok 1 ok 2 ok 3 not ok 4 # Failed test at t/nabeatzz.4.t line 21. # got: '[10]' # expected: 'dog' not ok 5 # Failed test at t/nabeatzz.4.t line 21. # got: '[20]' # expected: 'dog' # Looks like you failed 2 tests of 5.
このように、
Test::Baseを使う上でのtips
詳しくはperldoc Test::Baseで直接参照してもらうのが一番良いですが
テストデータの外部読み込み
spec_file($filepath);
上記を実行すると、
テストデータ中でのフィルタの指定
--- input chomp nabeatzz 1
上のように記述すると、
SKIP, ONLYフィールド
=== test --- ONLY --- input 1
ONLYという名称のフィールドがあった場合、
完成したNabeAtzz.pmとそのテスト
最後に、
package NabeAtzz;
use strict;
use warnings;
sub shout {
my ($class, $number) = @_;
return "fool" if ($number % 3 == 0) or $number =~ /3/;
return "dog" if $number % 5 == 0;
return "[$number]";
}
1;
use strict;
use warnings;
use Test::Base;
use NabeAtzz;
plan tests => 1*blocks;
filters {
input => [qw/chomp nabeatzz/],
expected => [qw/chomp/],
};
sub nabeatzz {
NabeAtzz->shout(shift)
}
run {
my $block = shift;
is_deeply $block->input, $block->expected;
};
__END__
=== test 1
--- input
1
--- expected
[1]
=== test 4
--- input
4
--- expected
[4]
=== test 19
--- input
19
--- expected
[19]
=== test 10 ( multiple of five
--- input
10
--- expected
dog
=== test 20 ( multiple of five
--- input
20
--- expected
dog
=== test 3 ( multiple of three
--- input
3
--- expected
fool
=== test 9 ( multiple of three
--- input
9
--- expected
fool
=== test 43 ( three is contained
--- input
43
--- expected
fool
=== test 301 ( three is contained
--- input
301
--- expected
fool
まとめ
今回はTest::Baseについて、
それでは、
