Test::ExceptionとTest::MockObjectを使用したテストを書いてみました。
それぞれのモジュールは下記のような機能を持っています。
Test::Exception
例外のテストを行なうモジュールです。
下記のようなメソッドが定義されています。
lives_ok { test_code() } "test_code()は例外を投げずに正常終了する"; dies_ok { test_code() } "test_code()は例外を投げ終了した"; throws_ok { test_code() } "ExceptionObject", "test_code()はExceptionObject例外を投げて終了した";
Test::MockObject
オブジェクトを偽装するモジュールです。
下記のようにTest::MockObjectのインスタンスを生成し、偽装メソッドを定義していきます。
my $mock = Test::MockObject->new; $mock->mock( 'foo' => sub { ok(1, 'foo() got called'); });
テスト
テストしたモジュールは、ItemManagerというものです。
package ItemManager; use strict; use warnings; sub new { my $class = shift; bless {@_}, $class; } sub put_item { my $self = shift; my ($item_number, $count) = @_; # 1 $self->{item_hash}->{$item_number} ||= 0; my $rest = $self->{item_hash}->{$item_number}; if ( $rest < $count ) { # 2 die bless {}, "ItemShortageException"; } # 3 for (my $i = 0; $i < $count; $i++) { eval { # 4 $self->{item_unit}->put_item( $item_number ); }; if ($@) { #5 die bless {}, "ItemUnitJammedException"; } } # 6 } sub set_item_unit { my $self = shift; my ($item_unit) = @_; $self->{item_unit} = $item_unit; } 1;
主にput_item()メソッドをテストしています。
put_item()メソッドでは、条件が合わない場合、例外を投げます。
この例外はTest::Exceptionでテストします。
また、ItemManagerは、ItemUnitオブジェクトを保有しています。
ItemUnitはまだ実装していないことを想定しているので、
Test::MockObjectで偽装します。
テストケースでは下記の処理経路を辿るようにしました。
番号はコメントで記述されているものです。
- 1 - 2 (例外終了)
- 1 - 2 - 3 - 4 - 5 (例外終了)
- 1 - 2 - 3 - 4 - 3 - 6 (正常終了)
use strict; use warnings; My::Test->runtests; package My::Test; use base qw/Test::Class/; use Test::More; use Test::MockObject; use Test::Exception; use ItemManager; sub make_fixture : Test(setup) { my $self = shift; $self->{item_manager} = ItemManager->new( item_hash => { 10 => 10 } ); } sub test_instance : Test(no_plan) { my $im = shift->{item_manager}; isa_ok $im, 'ItemManager'; } sub test_put_item : Test(no_plan) { my $self = shift; my $im = $self->{item_manager}; # Itemが足りないとき、ちゃんと例外を投げるかどうか( 1 -2 ) throws_ok { $im->put_item( 10, 11 ); } 'ItemShortageException', '$im->put_item()は,ItemShortageExceptionを投げて終了した。'; # forループが正常に終わるか ( 1 - 2 - 3 - 4 - 5 ) my $mock = Test::MockObject->new; $mock->mock('put_item' => sub { ok(1, 'put_item() got called'); }); $im->set_item_unit($mock); $im->put_item( 10, 1 ); # item_unitが例外を投げた時、ItemManagerが正常に例外を投げるか ( 1 - 2 - 3 - 4 - 3 - 6 ) $mock->mock('put_item' => sub { die "UnitJammedException"; }); throws_ok { $im->put_item( 10, 1 ); } "ItemUnitJammedException", '$im->put_item()は,ItemUnitJammedExceptionを投げて終了した。'; }
正常にテストが完了したら下記のように結果が表示されます。
$ prove -lv t/00.t Name "main::running_under_some_shell" used only once: possible typo at /usr/bin/prove5.10.0 line 3. t/00.t .. # # My::Test->test_instance ok 1 - The object isa ItemManager # # My::Test->test_put_item ok 2 - $im->put_item()は,ItemShortageExceptionを投げて終了した。 ok 3 - put_item() got called ok 4 - $im->put_item()は,ItemUnitJammedExceptionを投げて終了した。 1..4 ok All tests successful. Files=1, Tests=4, 0 wallclock secs ( 0.05 usr 0.02 sys + 0.18 cusr 0.04 csys = 0.29 CPU) Result: PASS
参考
- 作者: 牧大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/10
- メディア: 大型本
- 購入: 23人 クリック: 465回
- この商品を含むブログ (112件) を見る
- 作者: 大塚知洋,吉津 卓保,nanto_vi,武者晶紀,縣俊貴,下岡 秀幸,山本 陽平,高林 哲,小飼弾,はまちや2,角田直行,藤本 真樹,松原 敦,大谷弘喜,杵渕 聡,西田 圭介,田中洋一郎,ミック,伊藤直也,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2008/08/22
- メディア: 大型本
- 購入: 7人 クリック: 45回
- この商品を含むブログ (29件) を見る