読者です 読者をやめる 読者になる 読者になる

その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

「メソッドに対してテストをするな」という話題について

―「間違っているかもしれないので,その時はこの銃で僕を撃ってくれ,良いね?」


[2014-05-19T17:48:28Z 追記]
http://a-suenami.hatenablog.com/entry/2014/05/17/131326
補足してもらったので読むと良いと思います.
わかっちゃはいたけれど上手く言語化できていなかった部分,あるいはわかっていない部分について言及されていたので参考になりました.ありがとうございます.

いやーつーかさー,「『メソッドに対するテスト』っていう言葉自体がわかりにくくね?」っていうのはその文言を見たり,この文章書いている間もずっと思ってて,つまり端的に言うとそういう事を言いたかったはずなのに,今このエントリ読み返すとそうした[趣主]旨から完全にズレててメッチャ違和感あるな! ってなりました.俺のバカ!

これを仮に言い換えるとしたら「内部構造に対するテスト」とかそういうのになるんですかね? 難しいな!
[追記ここまで]


良いテストを書くための指針として「メソッドに対してテストをするな」という言葉がありますが,これは「ユニットテストに対して分かりやすい (説明的な) 名前を付けろ」ということを言っているのであって,「メソッド単体のテストをするな,ある機能を満足するシナリオについてテストをせよ」と言っている訳ではないと思います.


極端なコード例ですが (追記: ***念の為*** 書きますけどfoo()というふざけた関数名は伏線ですので回収されるまで待つこと.だから「極端」って言ってるんですねハイ),

sub foo {
    my $num = shift;
    return ++$num;
}

という,受け取った引数をインクリメントしたものを返す関数があったとして,これに対して

subtest 'test foo()' => sub {
    is foo(1), 2;
};

というテストを書いたとしても,この“test foo()”という説明からは,このテストが何についてテストをしているのかがわかりません.これがいわゆる「メソッドに対してテストをしている」状態なのだと思います.

そこでこのテストを

subtest '#foo()' => sub {
    subtest '引数がインクリメントされる' => sub {
        is foo(1), 2;
    };
};

という風に書き換えてやると,テストの対象と期待する動作が明確になってマシになった感じがします.これがいわゆる「メソッドに対するテスト」ではない状態と言えるのでは無いでしょうか.
(というか,foo()とかいうふざけたメソッド名はいったいなんだ!!! 後述!!!)


そもそも,「あるシナリオを満足しているかどうかをチェックするテスト」と「メソッドに対するユニットテスト」はそれぞれ負っている責務が異なるように感じています (前者は「振る舞いテスト」と呼ばれるかもしれません) .
振る舞いテストは「ビジネスが要求するシナリオが壊れている (満足していない) 時に教えてくれる存在」であり,これを満足しない限りはコードを成果物としてリリースが出来ないという受け入れテスト的な側面を有している一方,
ユニットテストは「どこが原因でそのシナリオが壊れているのか」あるいは「どうすればそのシナリオを満足することができるのか」といった問題を切り分ける為に役に立つ情報を提供してくれたり,コードの一部を修正した時にそのメソッドが提供する単一機能の挙動が変わってしまってはいないかをスピーディに確認出来るのでコードに対する変更を恐れずに済んだり,といったメリットを享受する為の開発者向けのテストであるように思います.ただし,ユニットテストだけでは「シナリオを満足しているかどうか」を担保することは出来ないと思います.

端的に言うと,ソフトウェアがちゃんと動いているかどうかを担保する為には振る舞いテストだけで十分という事になりますが,その開発の効率やコードの品質を向上させたい場合にはユニットテストが必要になるという事になると感じています.
たいていの場合は,開発にスピード感が無ければプロジェクトは死ぬし,コードの品質が劣っているとプロジェクトは地獄と化した末に死ぬので,結果的にユニットテストも書くことになると思います.

従って,振る舞いテストとユニットテストはどちらか一方だけを書けば良いというものではなく,つまり「メソッドに対してテストをするな」という言葉は「単一メソッドに対するユニットテストをするな」という論旨のものではない,という認識です.


ところで,メソッドの命名が適切になされていれば (つまりメソッドの名前が説明的であれば) ,いわゆる「メソッドに対するテスト」を書いたとしてもおのずと (ある程度) 説明的なテストになると思うのですが,どうか.
いわゆる「メソッドに対するテスト」を書いてそのテストの情報量が低い場合は,プロダクションコードの方にも何らかの問題があるように感じていますが,どうでしょうね.という事を上のコード例を書いている時に思いました.