CPAN形式を用いた開発手法(コーディングからテストの流れ)

CPAN形式を用いた開発手法(コーディングからテスト)

CPANを一番最初に使い始めたのは数年前ブックマークサイトを作ったとき
HTML::ExtractContentがきっかけでそれ以降、色々なモジュールを試すようになった。
使用していて分かる通り、
CPAN形式というのは、通常のアプリケーションの開発にもとても機能的に動作する。
ソースなどはモダンPerl

メリット

1.開発〜テスト〜コミットの一連の流れが自然

2.形式の統一化。ーーレイアウトが統一されていることで共有しやすい

3.パッケージング方法がメンテナンス/開発されている
CPAN形式はPerlという言語そのものと結合している。また後方互換にも優れている。

4.依存関係の自動検知/解決方法が存在している

5.最終的に配布可能

レイアウト

Example-Software/
	Makefile.PL/
	t/
		00_compile.t
	lib/
	  Example/
	  	Software.pm

ソース

#lib/Example/Software.pm
package Example::Software;
use strict;
our $VERSION = '0.00001';
1;
#Makefile.PL <- testを省略してもt/ディレクトリ直下の.t.ファイルをテストととしてみなす
use strict;
use inc::Module::install;
name('Example-Software');
all_from('lib/Example/Software.pm');
#test('t/*.t t/*/*.t');
WriteAll;
#t/00_compile.t
use strict;
use Test::More;
plan(tests=>1);
use_ok("Example::Software");

モジュールのインストール

cpan install inc::Module::install
cpan install Test::Harness;
cpan install Test::More;

実行方法

make clean
perl Makefile.PL
make
make test

proveによる実行方法

prooveはTest::Harnessに梱包されているツール。
テスト結果のカラー出力と、テスト実行時間の計測が可能。

#テストディレクトリ(t)配下をすべて実行する。※すべてのテストを実行する
cd Example-Software
prove -vlr t

Test::Moreの基本的な関数

#値が真か確認
ok($true_or_false,'test');

#classがuseできるか確認
use_ok($class,@args);

#構造体が一緒
use strict;
use Test::More;
plan(tests=>1);
my $hash = {foo=>1};
my $expect = {foo=>2}
is_deeply($hash,$expect,'構造体が一致') or dialog(explain($hash));

#期待値と一致するか
is($value,'期待値','$valueが期待値と一致するか確認');

#正規表現と一致するか
like($value,qr/正規表現/,'$valueが正規表現と一致するか確認');

具体的なテスト例

例)動作させるコード

sub is_even{
	my $n = shift;
	if($n != int($n)){
		die "not integer";
	}
	if($n < -100 || $n > 100){
		die "-100 ~ 100";
	}
	return $n % 2 == 0;
}

例)それに対するテストコード

use strict;
use warnings;
use Test::More;
plan(tests=>9);
use Test::Exception;
my @data = (
	[-42,1],
	[4,1],
	[-11,0],
	[97,0],
	[34,1],
	[-21.5, qr/not integer/],
	[4.1, qr/not integer/],
	[4.2, qr/not integer/],
  [1.6, qr/not integer/],
	[-1000,qr/-100~100/],
	[-101,qr/-100~100/],
	[-100,1],
	[-99,0],
);
foreach my $testcase(@data){
	my($n,$expect_even)=@$testcase;
	if(ref $expect_even eq 'Regexp'){
    throws_ok{is_even($n)}$expect_even,
    	(defined $n ? "(undef)" : $n). "should throw an exception";
	}elsif($expect_event){
		ok(is_even($n),"$n should be even");
	}else{
		ok(!is_even($n),"$n should not be even");
	}
}