ziguzagu.org

複雑な構造のデータをテストする

まだ、『PerlTesting』はChapter 2。

今までやってきたテストは、単純なスカラー値のテストと関数のテストのみですが、もっと複雑なデータをテストしたい場合があります。

Test::More::is_deeply

:is_deeply:リファレンスを追いかけて複雑な構造のデータを比較して同じかどうかテストする

たとえば、リスト>ハッシュ>スカラー、リストのテスト。

#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 1;

my $list1 = [
    {
        name   => 'Tom'
        family => [qw/father mother dog/],
    },
    {
        name   => 'Bob',
        family => [qw/father mother sister cat/],
    }
];

my $list2 = [
    {
        name   => 'Tom',
        family => [qw/father mother cat/],
    },
    {
        name   => 'Bob',
        family => [qw/father mother brother cat/],
    }
];

is_deeply($list1, $list2, 'family structure');

実行結果は、

[omae@colinux]% prove -v deeply2.t                             [~/perl/test/02]
deeply2....1..1
not ok 1 - family structure
###   Failed test 'family structure'
###   in deeply2.t at line 29.
###     Structures begin differing at:
###          $got->[0]{family}[2] = 'dog'
###     $expected->[0]{family}[2] = 'cat'
### Looks like you failed 1 test of 1.
dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
deeply2.t      1   256     1    1 100.00%  1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.

どの部分が違っていたか細かく表示されます。ただ、2箇所以上の失敗データが詰まっていても、最初に失敗した時点でテストをやめてしまうのか、結果には表示されません。

で、その悩みを解決してくれるのが次のText::Differeneces

Test::Differences

Test::More::is_deeply()では、データ内で複数の箇所が異なって(テストに失敗して)いても表示される結果はひとつだけでしたが、Test::Differencesを使うとdiff表示のようにテスト結果を表示してくれます。と、言いたいのですが、先の自前サンプルだとちゃんと表示されなかったので、とりあえず本のサンプルそのまま試します。

#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 1;
use Test::Differences;

my $list1 = [
    [
        [ 48, 12 ],
        [ 32, 10 ],
    ],
    [
        [ 03, 28 ],
    ],
];

my $list2 = [
    [
        [ 48, 12 ],
        [ 32, 11 ],
    ],
    [
        [ 03, 29 ],
    ],
];

eq_or_diff($list1, $list2, 'existential equivalence');

テスト結果表示はこんな感じ(ちょい長い…)。

[omae@colinux]% prove -v differences.t                         [~/perl/test/02]
differences....1..1
not ok 1 - existential equivalence
###   Failed test 'existential equivalence'
###   in differences.t at line 27.
### +----+-----------+-----------+
### | Elt|Got        |Expected   |
### +----+-----------+-----------+
### |   0|[          |[          |
### |   1|  [        |  [        |
### |   2|    [      |    [      |
### |   3|      48,  |      48,  |
### |   4|      12   |      12   |
### |   5|    ],     |    ],     |
### |   6|    [      |    [      |
### |   7|      32,  |      32,  |
### *   8|      10   |      11   *
### |   9|    ]      |    ]      |
### |  10|  ],       |  ],       |
### |  11|  [        |  [        |
### |  12|    [      |    [      |
### |  13|      3,   |      3,   |
### *  14|      28   |      29   *
### |  15|    ]      |    ]      |
### |  16|  ]        |  ]        |
### |  17|]          |]          |
### +----+-----------+-----------+
### Looks like you failed 1 test of 1.
dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test   Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
differences.t    1   256     1    1 100.00%  1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.

で、Test::More::is_deeply()で試した、自作のハッシュ交じりのもう少し複雑データはこうなった。

[omae@colinux]% prove -v differences2.t                        [~/perl/test/02]
differences2....1..1
not ok 1 - family structure

###   Failed test 'family structure'
###   in differences2.t at line 30.
### +----+----------------------+----------------------+
### | Elt|Got                   |Expected              |
### +----+----------------------+----------------------+
### |   0|family,name           |family,name           |
### *   1|ARRAY(0x814cc20),Tom  |ARRAY(0x816054c),Tom  *
### *   2|ARRAY(0x82b2f60),Bob  |ARRAY(0x82b3038),Bob  *
### +----+----------------------+----------------------+
### Looks like you failed 1 test of 1.
dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test    Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
differences2.t    1   256     1    1 100.00%  1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.

HashのValueがなんだか違うことだけはわかるけど…。で、PODみてみるとこう書いてあった。

These functions assume that you are presenting it with "flat" records, looking like:

- scalars composed of record-per-line
- arrays of scalars,
- arrays of arrays of scalars,
- arrays of hashes containing only scalars

All of these are flattened in to single strings which are then compared for differences. Differently data structures can be compared, as long as they flatten identically.

ここで言われてる"flat"なデータってどんなもんじゃい、というのはまぁ書いてあるとおりです。まぁ、リストはスカラー、リスト、ハッシュを含んでもOK、でもハッシュは単純なスカラーだけっぽいです。あと、日本語文字列は予想通り表示されず、\x00なかたちに変換され表示されます。

Test::LongString

Test::LongStringでバイナリデータの比較テストができます。Text::Differencesと何が違うかは表示できない制御コードだとかをエスケープして文字列として表示してくれたりします。

#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 1;
use Test::LongString;

my $string1 = <<"END1";
test
todo
END1

my $string2 = <<"END2";
text
todo
END2

is_string($string1, $string2, 'are they the same?');

結果。

[omae@colinux]% prove -v longstring.t                          [~/perl/test/02]
longstring....1..1
not ok 1 - are they the same?
###   Failed test 'are they the same?'
###   in longstring.t at line 17.
###          got: "test\x{0a}todo\x{0a}"
###       length: 10
###     expected: "text\x{0a}todo\x{0a}"
###       length: 10
###     strings begin to differ at char 3
### Looks like you failed 1 test of 1.
dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test  Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
longstring.t    1   256     1    1 100.00%  1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.

あんまし使わないかも…。