自動テストでチェックが失敗したときの説明を見やすくする

これは Perl Advent Calendar 2022 16日目の記事です。

昨日の記事は@hkunoさんのぜんぜんわからない。俺達は雰囲気で perl -p -i.bak をやっている でした。


Test2::Suiteis 関数とTest2::Tools::Compareに登場する比較関数を組み合わせると、ネストしたデータ構造のチェックを(伝統的なスクリプト言語にしては比較的)分かりやすく記述できます。

cpm install Test2::Suite

サンプルコードです。 ステートレスな関数を中心に設計しているなら、道具立てとしては十分でしょう。

このコードの後半に記述したチェックには誤りがあるので、失敗します。 失敗した理由の説明が不格好になってしまうところが本題です。

失敗したチェックの説明が読みにくい

次のように、失敗したチェックは表形式で説明が表示されるのですが、途中で折りたたみが生じていてとても読みにくいのです。

一列目は情報パス(PATH)、二列目は実測値(GOT)、三列目は比較演算子(OP)、四列目は期待値(CHECK)、という、記号を駆使した文字列による表形式です。 行番号(LNs)も付いてるのでだいぶわかりやすいと思います。 ただ、見た目には不満があります。

  • 表の幅が狭くて、それぞれの列幅も狭くなっている
  • 情報パス、実測値、期待値一部の行に折りかえしが生じている(行がずれないように折りかえしてるのは逆にすごい)

私たちのチームにはちゃんと自動テストを作成する文化があるため、日々の開発で感じる不満はできるだけなくしておきたいところです。

表の幅を決めているところを突き止める

テスト結果をTAP(Test Anything Protocol)形式で出力するTest2::Formatter::TAP(Perl5の標準添付モジュール)が、Term::Tableを使用して出力しています。 出力しているのはこの辺、幅を決めているのはこの辺この辺です。

ソースコードを眺めたところ、インストールされているモジュールがあればそれを、無ければ環境変数を、それも無ければ初期値を、という論理になっています。 上記の結果は、初期値の80文字が採用されている状態でした。

まとめ:表の幅を(列の幅を)広げる

普通はログを表示する環境に長い文字列を折りたたむ機能があります。 中途半端に折りたたまれるより、環境変数TABLE_TERM_SIZE=2000 くらい指定して折りたたみが起きないようにするといいでしょう。 列を指定して幅を調整する方法はないのですが、個人的には満足です。 たぶん、GitHub ActionsなどのCIで実行するときも役立ちます。

ちなみに、Term::ReadKeyやTerm::Size::Anyをインストールしても、端末エミュレータの表示幅を使用してくれませんでした。 理由はわからない

TABLE_TERM_SIZE=200 plenv exec prove --nocolor --trap -lvrfomp example.pl
example.pl .. 
# Seeded srand with seed '20221213' from local date.
not ok 1

# Failed test at example.pl line 66.
# +------------------------------+-------------------+----+--------+--------+
# | PATH                         | GOT               | OP | CHECK  | LNs    |
# +------------------------------+-------------------+----+--------+--------+
# |                              | HASH(0x12d819a30) |    | <HASH> | 63, 66 |
# | {abcdefghijklmnopqrstuvwxyz} | def               | eq | defg   | 64     |
# +------------------------------+-------------------+----+--------+--------+
not ok 2

# Failed test at example.pl line 80.
# +-------------------------------------------------+----------------------------+----+--------+--------+
# | PATH                                            | GOT                        | OP | CHECK  | LNs    |
# +-------------------------------------------------+----------------------------+----+--------+--------+
# |                                                 | HASH(0x12e7b80a8)          |    | <HASH> | 77, 80 |
# | {first_field}                                   | HASH(0x12e7bf288)          |    | <HASH> | 78     |
# | {first_field}{second_field}                     | HASH(0x12e7b8060)          |    | <HASH> | 76     |
# | {first_field}{second_field}{third_field}        | HASH(0x12d80aa68)          |    | <HASH> | 74     |
# | {first_field}{second_field}{third_field}{label} | abcdefghijklmnopqrstuvwxyz | == | 124    | 72     |
# +-------------------------------------------------+----------------------------+----+--------+--------+
not ok 3

# Failed test at example.pl line 87.
# +------+--------------------+----+---------+--------+
# | PATH | GOT                | OP | CHECK   | LNs    |
# +------+--------------------+----+---------+--------+
# |      | ARRAY(0x12d819a18) |    | <ARRAY> | 82, 87 |
# | [0]  | 1                  | == | 2       | 83     |
# | [1]  | 2                  | == | 3       | 84     |
# | [2]  | 3                  | == | 4       | 85     |
# +------+--------------------+----+---------+--------+
not ok 4

# Failed test at example.pl line 94.
# +------+--------------------+---------+------------------+--------+
# | PATH | GOT                | OP      | CHECK            | LNs    |
# +------+--------------------+---------+------------------+--------+
# |      | ARRAY(0x12d819a18) |         | <BAG>            | 89, 94 |
# | [*]  | <DOES NOT EXIST>   |         | 4                | 90     |
# | [2]  | 3                  | !exists | <DOES NOT EXIST> |        |
# +------+--------------------+---------+------------------+--------+
not ok 5

# Failed test at example.pl line 102.
# +-------+-----------------------+----+--------------------------------+---------+
# | PATH  | GOT                   | OP | CHECK                          | LNs     |
# +-------+-----------------------+----+--------------------------------+---------+
# |       | Foo=HASH(0x12e7d55f8) |    | <OBJECT>                       | 96, 102 |
# | foo() | 2022                  | eq | abcdefghijklmnopqrstuvwxyz2021 | 98      |
# +-------+-----------------------+----+--------------------------------+---------+
1..5
Dubious, test returned 5 (wstat 1280, 0x500)
Failed 5/5 subtests 

Test Summary Report
-------------------
example.pl (Wstat: 1280 Tests: 5 Failed: 5)
  Failed tests:  1-5
  Non-zero exit status: 5
Files=1, Tests=5,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.03 cusr  0.01 csys =  0.05 CPU)
Result: FAIL

明日の記事は@doikojiさんです。引き続きよろしくお願いします。