Help us understand the problem. What is going on with this article?

Test::Moreをワンライナーで普段使い

More than 3 years have passed since last update.

Perlでテストを書くときに使われるTest::Moreは「Perlを学ぶとき、真っ先に覚えたいモジュール」の一つです。でも意外に使い慣れてなかったりします。

  1. Test::Moreを使ってテストを書くべき。
  2. Test::Moreはテストを書くときぐらいしか使わない。
  3. テストを書くためだけに、Test::Moreを覚えるのは億劫。

見事にテストを書くこと自体ネガティブになっちゃいますね。ネガティブ・サイクルが回っちゃいますね。このままだとTest::Moreを覚えずにprintf()頼りのテストばかり書いたり、もしかしたらテストも書かないまま一生グルグル回っちゃいます。

まだあります。条件1のところで「でもTest::Moreを使ってテストを書くべきだよ!」と強く言われて一回は書いたとしましょう。その時は多少なりTest::Moreを覚えたんです。でも条件2:テストを書くときぐらいしか使わないから忘れちゃって、条件3':テストを書くためだけに、Test::Moreを思い出すの億劫になります。帰ってきたネガティブ・サイクルです。

Test::Moreを普段使いして、条件2を壊しちゃいましょう。「Perlを学ぶとき、真っ先に覚えたいモジュール」でも、簡単なお試しコードを書くときに使おうよと勧めています。ここでは追加で、コマンドラインから直接perlコードを動かしてみるワンライナー(One Liner)でも、Test::Moreを使うことをお勧めします。

ワンライナーでモジュールを使うまで

perlコマンド実行時に-eスイッチをつけると、スクリプトファイルではなく、コマンドライン上で直接コードを書いて実行させることができます。ワンライナーと呼ばれます。例えば「substr('abcde', 2, 1)を実行したら、2文字目と3文字目、どちらが返ってくるんだっけ?」と思ったら、次のようにして実行することができます。

c:\> perl -e "print substr('abcde', 2, 1);"
c

ワンライナーでモジュールを使うこともできます。例えば、Webコンテンツを取得するのに役立つLWP::UserAgentを使ってみましょう。コード内でuseしてもいいのですが、-Mスイッチを使うのが一般的です。

c:\> perl -MLWP::UserAgent -e "print LWP::UserAgent->new->get('http://example.com/');"
HTTP::Response=HASH(0x2418214)

おや、オブジェクトが返ってきたみたいですね。でもこの中身を見ないと、どんなコンテンツを取得できたのかとか、成功したのか失敗したのかもよくわかりません。そんな時はまずData::Dumperですね。ワンライナーでも使えます。LWP::UserAgentとの併用もできます。-Mスイッチを二つ並べるのです。

c:\> perl -MData::Dumper -MLWP::UserAgent -e "print Dumper( LWP::UserAgent->new->get('http://example.com/') );"
$VAR1 = bless( {
  '_content' => 'Can\'t connect to example.com:80 (10060)

LWP::Protocol::http::Socket: connect: 10060 at C:/strawberry/perl/vendor/lib/LWP/Protocol/http.pm line 51.
',
  '_rc' => 500,
  (略)
}, 'HTTP::Response' );

接続に失敗した、といったデータが格納されているようです(例示専用のドメイン名の'example.com'にアクセスしようとすればそうなります)。どうでしょう。-Mスイッチと-eスイッチを使うワンライナーには馴染めたでしょうか?

perl -M使用するモジュール名 -e "実行したいコード"

ワンライナーでTest::Moreを使う

Test::Moreも、他のモジュールと同じように使えます。

perl -MTest::More -e "実行したいコード"

Test::Moreのいろいろな命令を使ってみましょう。

print代わりにdiagを使う

Test::Moreにはprint文のように(正確にはprint STDERRのように)使えるdiagという命令があります。まずsubstrの実行結果を確かめたワンライナーを思い出しましょう。

c:\> perl -e "print substr('abcde', 2, 1)"
c

printの代わりにTest::Moreのdiagを使うと、次のようになります。

c:\> perl -e -MTest::More "diag substr('abcde', 2, 1)"
# c

ほぼ同じように使えますね。diagの出力は、行頭に「# 」が入っていて、スクリプト内でprintなどしているときには区別がつきやすくなります。もう一つ、出力対象が未定義値の時はundefが出力されます。これは「空文字列かundefか」が区別できて便利です。

c:\>perl -MTest::More -e "diag undef"
# undef

Dumper代わりにexplainを使う

Data::DumperのDumper命令のように、リファレンスを文字列に変換してくれるexplainという命令があります。Dumperを使った例は以下でした。

c:\> perl -MData::Dumper -MLWP::UserAgent -e "print Dumper( LWP::UserAgent->new->get('http://example.com/') );"
$VAR1 = bless( {
  '_content' => 'Can\'t connect to example.com:80 (10060)

LWP::Protocol::http::Socket: connect: 10060 at C:/strawberry/perl/vendor/lib/LWP/Protocol/http.pm line 51.
',
  '_rc' => 500,
  (略)
}, 'HTTP::Response' );

Test::Moreのexplainを使うと、次のようになります。

C:\>perl -MTest::More -MLWP::UserAgent -e "diag explain( LWP::UserAgent->new->get('http://example.com/') );"
# bless( {
#   '_content' => 'Can\'t connect to example.com:80 (10060)
#
# LWP::Protocol::http::Socket: connect: 10060 at C:/strawberry/perl/vendor/lib/LWP/Protocol/http.pm line 51.
# ',
#   '_headers' => bless( {
#       (略)
# }, 'HTTP::Response' )

ほぼ同様の出力が得られてますね。explainでは、キーがアルファベット順で出力されていたり、$VAR1 =が省略されていたり、インデントが調整されていたりと少し見た目が整えられています。

isで期待している値になるか確かめる

substr('abcde', 2, 1)を実行した時に「2文字目が返って来るか」までワンライナーで確認させようと思ったら、次のようになるでしょう。

c:\> perl -e "$_ = substr('abcde', 2, 1); if ($_ eq 'b') { print 'ok' } else { print 'not ok' }"
not ok

Test::Moreであれば、isという命令を使えます。

C:\> perl -MTest::More -e "is(substr('abcde', 2, 1) , 'b')"
not ok 1
#   Failed test at -e line 1.
#          got: 'c'
#     expected: 'b'
# Tests were run but no plan was declared and done_testing() was not seen.

Test::Moreのisを使うと、結果に加えて、not okの時は実際の値(got)と期待していた値(expected)も出力してくれます。結果がokの時にはこれらは出力されないのもよい感じです。isの逆で、値が異なることを確かめるためのisntという命令もあります。

likeで正規表現にマッチするか確かめる

たとえば今日が週末かどうか、確かめてみましょう。sat|sunという正規表現を使えばよさそうです。

C:\> perl -e "$_ = localtime; if ($_ =~ /sun|sat/i) { print 'ok' } else { print 'not ok' }"
not ok

Test::Moreには、この正規表現とマッチするかを調べるためのlikeという命令があります。

C:\> perl -MTest::More -e "$_ = localtime; like($_, qr/fri/i)"
not ok 1
#   Failed test at -e line 1.
#                   'Fri Mar  6 19:38:13 2015'
#     doesn't match '(?^i:sat|sun)'
# Tests were run but no plan was declared and done_testing() was not seen.

週末ではなかったことと、localtimeの出力(は金曜日だったこと)が分かります。likeの逆で、値が正規表現にマッチしないことを確かめるためのunlikeという命令もあります。

is_deeplyで配列や連想配列などが一致するか確かめる

配列リファレンスや連想配列(ハッシュ)リファレンスの中を、再帰的に比較してくれるis_deeplyという命令もあります。例えば、まず次のようなワンライナーを考えましょう。第一引数を名、第二引数を姓として、連想配列リファレンスにしています。diagprintのようなもの、explainはData::DumperのDumperのようなものでした。

C:\> perl -MTest::More -e "$_ = {'FirstName' => $ARGV[0], 'LastName' => $ARGV[1]}; diag explain($_)"

実際に引数をつけて実行すると、次のようになります。

C:\> perl -MTest::More -e "$_ = {'FirstName' => $ARGV[0], 'LastName' => $ARGV[1]}; diag explain($_)" Makio Tsukamoto
# {
#   'FirstName' => 'Makio',
#   'LastName' => 'Tsukamoto'
# }

例えば名(LastName)だけが期待する値と一致するか確認するのならば、isで充分です。

C:\> perl -MTest::More -e "$_ = {'FirstName' => $ARGV[0], 'LastName' => $ARGV[1]}; is($_->{'FirstName'}, 'Makio')" Makio Tsukamoto
ok 1

全データを確認するとなると、isがどんどん並んでいくことになります。そんな時はis_deeplyです。

C:\>perl -MTest::More -e "$_ = {'FirstName' => $ARGV[0], 'LastName' => $ARGV[1]}; is_deeply($_, {'FirstName' => 'Makio', 'LastName' => 'Tsukamoto'})" Makio Tsukamoto
ok 1

もちろん、not okの時は、詳細も出力されます。

C:\> perl -MTest::More -e "$_ = {'FirstName' => $ARGV[0], 'LastName' => $ARGV[1]}; is_deeply($_, {'FirstName' => 'Larry', 'LastName' => 'Wall'})" Makio Tsukamoto
not ok 1
#   Failed test at -e line 1.
#     Structures begin differing at:
#          $got->{FirstName} = 'Makio'
#     $expected->{FirstName} = 'Larry'

そのほかのTest::Moreの命令を調べる

もうTest::Moreをワンライナーで使うことには慣れたでしょうか?これらの他に役立つ命令があるか気になり始めたでしょうか?もしそうだったら、そろそろTest::Moreのドキュメントを眺めてもいいころです。少し旧い版が元ですが、perldoc.jpコミュニティによる和訳もあります。

まとめ

Test::Moreは簡単なお試しコードを書くときにはもちろん、コマンドラインからのワンライナーでも使えるモジュールです。explainis_deeplyなどの命令は間違いなく便利ですし、他の命令も、使わない場合より便利かどうかはともかく、使うのが手間とか不便ということはありません。

Test::Moreを使い慣れて、その出力を見慣れておくと、テストを書くのはずっと気楽なことになるでしょう。テストを書くと、スクリプトやアプリケーションをメンテナンスし続け、更新していくことが少し気楽になります。

命令は、必要になったものから一つずつ覚えていけばよいと思います。ぜひテストのときだけではなく、Test::Moreを普段使いしてみてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away