―「間違っているかもしれないので,その時はこの銃で僕を撃ってくれ,良いね?」
良いテストを書くための指針として「メソッドに対してテストをするな」という言葉がありますが,これは「ユニットテストに対して分かりやすい (説明的な) 名前を付けろ」ということを言っているのであって,「メソッド単体のテストをするな,ある機能を満足するシナリオについてテストをせよ」と言っている訳ではないと思います.
極端なコード例ですが,
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()
とかいうふざけたメソッド名はいったいなんだ!!! 後述!!!)
そもそも,「あるシナリオを満足しているかどうかをチェックするテスト」と「メソッドに対するユニットテスト」はそれぞれ負っている責務が異なるように感じています (前者は「振る舞いテスト」と呼ばれるかもしれません) .
振る舞いテストは「ビジネスが要求するシナリオが壊れている (満足していない) 時に教えてくれる存在」であり,これを満足しない限りはコードを成果物としてリリースが出来ないという受け入れテスト的な側面を有している一方,
ユニットテストは「どこが原因でそのシナリオが壊れているのか」あるいは「どうすればそのシナリオを満足することができるのか」といった問題を切り分ける為に役に立つ情報を提供してくれたり,コードの一部を修正した時にそのメソッドが提供する単一機能の挙動が変わってしまってはいないかをスピーディに確認出来るのでコードに対する変更を恐れずに済んだり,といったメリットを享受する為の開発者向けのテストであるように思います.ただし,ユニットテストだけでは「シナリオを満足しているかどうか」を担保することは出来ないと思います.
端的に言うと,ソフトウェアがちゃんと動いているかどうかを担保する為には振る舞いテストだけで十分という事になりますが,その開発の効率やコードの品質を向上させたい場合にはユニットテストが必要になるという事になると感じています.
たいていの場合は,開発にスピード感が無ければプロジェクトは死ぬし,コードの品質が劣っているとプロジェクトは地獄と化した末に死ぬので,結果的にユニットテストも書くことになると思います.
従って,振る舞いテストとユニットテストはどちらか一方だけを書けば良いというものではなく,「メソッドに対してテストをするな」という言葉は「単一メソッドに対するユニットテストをするな」という論旨のものではない,という認識です.
ところで,メソッドの命名が適切になされていれば (つまりメソッドの名前が説明的であれば) ,いわゆる「メソッドに対するテスト」を書いたとしてもおのずと (ある程度) 説明的なテストになると思うのですが,どうか.
いわゆる「メソッドに対するテスト」を書いてそのテストの情報量が低い場合は,プロダクションコードの方にも何らかの問題があるように感じていますが,どうでしょうね.という事を上のコード例を書いている時に思いました.