単体テストにおけるテストメソッドのネーミングについて調べていたら、下記サイトでそれなりに賛同を得てるページがあったので、疎い英語力で読んでみました。
直訳しても「日本語でおk」が多かったので、自分の考えも補足として入れて要約しました。
メソッド名についてベストプラクティスを聞いていたURL
Unit test naming best practices
http://stackoverflow.com/questions/155436/unit-test-naming-best-practices
賛同を得ていたURL(こっちを読みました)
記事は古いですが、1年内で制限してヒットしたサイトでもリンクが貼られていました。
Roy Osherove の Naming standards for unit tests の翻訳
http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html
要約
テストのメソッド名を3つのパーツに分けるよ
[作業単位_テスト条件_想定行動]
作業単位
1つの作業単位って言うのは、パブリックメソッドだけじゃないよ。
次の3種類どれかに該当するユースケースも対象だよ。
- 値/例外を返す
- 状態の変更がシステムの状態を変化する
- サードパーティの呼び出し(Mockを使ってた時)
なので、作業単位はメソッド単位まで小さかったり、あるいはクラス単位になったり、ときには複数のクラスと同じ大きさになるよ。
下のようにね:
Public void Sum_NegativeNumberAs1stParam_ExceptionThrown()
Public void Sum_NegativeNumberAs2ndParam_ExceptionThrown()
Public void Sum_simpleValues_Calculated()
メソッド名を3つに分けた理由について
テスト名は特定の要件を表そう
単体テスト名は特定の要件を示した名前にしよう。
これはなんとかして、ビジネス要件か技術要件で形成すべきだよ。
いかなる場合でも、要件はテストケースを表し、各々が十分に小さいピースに分解されたほうがいいよ。
もしテストに要件が表されていないなら
- なぜ、あなたはそのコードを書いているの?
- なぜ、そのコードは存在するの?
開発者はめんどくさがり
テストを作成している開発者は、そのときは何をやっているのか理解できているので、テスト名に迷わず、無意味な名前や番号をつけるんだ。
こうやって作成されたテストは、誰もテストの名前を修正する時間やエネルギーを持っていないので、すぐに細い狭くて暗い道につながっちゃうよ。
(後から見た時に何を検証しているテストなのか判断できずゴミ化する)
開発者は、テスト内のアサーションメッセージを良くしようと悩まないよ。
だけど、テスト失敗時に表示される良いアサーションメッセージは、何が悪かったのか理解できる鍵となるんだ。
なんだけど、「良いアサーションメッセージを書きましょう」としたところで、ほとんどの開発者は難しいだろうね。残された唯一は、テストの名前だけに。。。
もしテスト名と不十分なエラーメッセージをメンテナンスをしない開発者がこれを見たら疑問に思うだろうか?
1年前の単体テストとコードを見てみよう。
テスト名は想定入力や状態とそれらの想定結果を含めるべき
要件を明確に表現ために、下の2点について考えよう。
- テスト実施時の入力値/現在の状態について
- それらの状態からの想定結果について
例
Public int Sum(params int[] values)
例えば、1000より大きい値は計算に含めない要件があったとする(つまり1000より大きい値は0とみなす)
テスト名「Sum_NumberBiggerThan1000」ってどう?
これは半分正解。他者が見たら、1000より大きい値が入力値だと仮定できますが、テストされている要件が理解できません。
「Sum_NumberIsIgnored」って名前はどう?
まだ十分ではないね。 想定される結果は分かるけど、どういった状況で無視されるのかが分からない。
このままでは、おそらく予想される値が何であるかコードを読まないといけないだろうね。
「Sum_NumberIgnoredIfBiggerThan1000」って名前はどう?
凄くいいね。私はテスト名を見ただけでテストの失敗原因が伝わる。
私はコードを見なくていい、しかも私は、このテストが解決しようとしている要件が正確に伝えられる。
テスト名はテストメソッドやテストクラス名を含めよう
テスト名は、要求だけでなくメソッド名をテスト名に。入れたほうがいいよ。
例えば:
[MethodName_StateUnderTest_ExpectedBehavior]
Public void Sum_NegativeNumberAs1stParam_ExceptionThrown()
Public void Sum_NegativeNumberAs2ndParam_ExceptionThrown ()
Public void Sum_simpleValues_Calculated ()
Public void Parse_OnEmptyString_ExceptionThrown()
Public void Parse_SingleToken_ReturnsEqualToeknValue ()
テスト内の変数ネーミング
変数名は想定した入力や状態を表そう。
変数名を変更することは簡単だけど、変数名を何にすべきかは注意して決めよう。
例えば:
Assert.AreEqual(-1, val)
よりは、
Assert.AreEqual(BAD_INITIALIZATION_CODE, ReturnCode, Method shold have returned a bad initialization code )
にしたほうがいいよ。
- マジックナンバーや戻り値の名前ではなく、数値や中身の意図を名前にしよう
- メッセージは空ではなく、どういった結果でなければいけないのかを書こう。
まとめ
- メソッド名は「メソッド名やユースケース」と「入力値(事前条件)」と「要件」で構成しよう。
- 「メソッド名やユースケース単位_テストの事前条件_想定される結果」
- テストは1年後を見据えてメンテナンスしよう
- マジックナンバーは使わず変数を使おう
- 変数名は戻り値の名前ではなく、数値や中身の意図を名前にしよう
- メッセージは空ではなく、どういった結果でなければいけないのかを書こう。
注意
開発者に対して極端な表現がありますが、目的はそこではないので、そこまで気になさらずに。
そう当てはまる人にも出会ったことがあるし、全くそんなことを感じさせない人にも出会ったことがあります。
ネーミングやコーディングルールといった類は
- 「おい、◯◯が抜けてるぞ!」
- 「実際の運用では、そこまで堅くしても回らないし」
- 「日本語でよくね?」
と、宗教戦争しやすいので、「こういった考えもあるのね」程度でお願いします。