Perl Hackers Hub

第51回 Test2で変わるモダンなテスト―拡張性を持ったテスティングフレームワークとTest2::V0の使い方(1)

この記事を読むのに必要な時間:およそ 5 分

比較の基本

あるコードが正しく動作しているかをテストするために,得られた値が期待された値と一致しているかどうか比較したいことがあります。ここまでの例では,文字列比較の結果を真偽値としてok関数に渡していました。is関数を使うと,文字列や数値,配列,ハッシュといったデータ構造まで,比較演算子を指定することなく直感的に2つの値を比較できます。

is ucfirst('foo'), 'Foo';
is [1..3], [1, 2, 3], '配列の比較';
is {map { ($_ => 1) } qw(a b)},
  {a => 1, b => 1}, 'ハッシュの比較';

もしコードが意図したとおりに動作していない場合,テストは失敗します。その場合に必要となるのは,どのテストがどのような状態で失敗したかという情報です。上記のテストをわざと失敗するように書き換えて実行します。

compare-fail.t

is ucfirst('foo'), 'bar';
is [1..3], [1, 2, 0], '配列の比較';
is {map { ($_ => 1) } qw(a b)},
  {a => 1, c => 1}, 'ハッシュの比較';

図1の実行結果を見ると,失敗したテストについて実際の値(GOT)と期待された値(CHECK)が出力されています。配列の何番目の要素が間違っていたのか,期待されたハッシュには存在しないキーがあるのかが,Test::Moreに比べると読みやすい形式になっています。

図1 compare-fail.tの実行結果

$ perl compare-fail.t
# Seeded srand with seed '20180625' from local date.
not ok 1
# Failed test at compare-fail.t line 3.
# +-----+----+-------+
# | GOT | OP | CHECK |
# +-----+----+-------+
# | Foo | eq | bar   |
# +-----+----+-------+
not ok 2 - 配列の比較
# Failed test '配列の比較'
# at compare-fail.t line 4.
# +------+-----+----+-------+
# | PATH | GOT | OP | CHECK |
# +------+-----+----+-------+
# | [2]  | 3   | eq | 0     |
# +------+-----+----+-------+
not ok 3 - ハッシュの比較
# Failed test 'ハッシュの比較'
# at compare-fail.t line 5.
# +------+------------------+---------+------------------+
# | PATH | GOT              | OP      | CHECK            |
# +------+------------------+---------+------------------+
# | {c}  | <DOES NOT EXIST> |         | 1                |
# | {b}  | 1                | !exists | <DOES NOT EXIST> |
# +------+------------------+---------+------------------+
1..3

ここまで,2つの値を比較する例を示しました。テストが複雑になってくると,値がundefではないことやハッシュのキーが存在するかどうかといったチェックをしたくなるかもしれません。Test2::V0では,期待される値の条件を指定するしくみが用意されています。以降でそれらについて解説していきます。

値の比較

is関数に渡した値はデフォルトで文字列による比較がされます(リファレンスを除く⁠⁠。明示的に文字列,数値,真偽値による比較を行いたい場合には,stringnumberbool関数を使います表1⁠。

表1 値の比較関数

関数名説明比較時の演算子
string文字列による比較$input eq $got
number数値による比較$input == $got
bool真偽値による比較($input xor $got) ? 0 : 1

また,次に示すコードにあるように,数値を文字列として比較した場合や,演算子を使って否定した場合の真偽値の扱われ方については注意が必要です。必要に応じて,numberbool関数を使い分けるとよいでしょう。

is lc('FOO'), string 'foo';
is '1.0', number 1;
isnt '1.0', 1; # 文字列による比較になるため両者は異なる
is !!0, bool 0;
is !!0, ''; # ここで期待している真偽値は空文字列になる
正規表現と条件式による比較

正規表現にマッチする文字列であることをテストする場合は,match関数を使います。また,条件式による比較を行いたい場合は,validator関数を使います。コードリファレンスを渡し,比較時に与えられた値は$_で参照できます。

is 'abc', match qr/^[a-z]+$/, 'すべてが小文字';
is 1, validator(sub { $_ > 0 }), '正の整数';
比較のショートカット

比較するデータ構造の要素が増えてくると,すべて列挙して比較するテストを書くのに時間がかかるため,もう少し省力的にテストをしたいことがあります。Test2::V0には,要素に対して真偽値による比較や,ハッシュのキーが存在するかどうかだけを確認するといった,頻出する値の比較を行うためのショートカットが用意されています表2⁠。

表2 値の比較ショートカット

関数名説明データ例
T真偽値によるチェックの結果,真である1,'a'
F真偽値によるチェックの結果,偽である0,'0','',undef
D値がundefではない1,0,'0',''
U値がundefであるundef
DF値がdefinedであるが真偽値による評価では偽である0,'0',''
E配列,ハッシュの要素が存在している{a => 1}は{a => E}にマッチ
DNE配列,ハッシュの要素が存在していない{a => 1}は{b => DNE}にマッチ
FDNE配列,ハッシュの要素が存在していない。またはその値が真偽値による評価では偽である{a => 0}は{a => FDNE, b => FDNE}にマッチ

著者プロフィール

秋山卓巳(あきやまたくみ)

1995年北海道生まれ。YAPC::Asia 2011にて当時高校生としてYAPCスピーカーデビュー。2017年より株式会社はてなに入社後,マンガサービスに携わる。

Twitter:@akiym
URL:https://akiym.com/