你的浏览器还没开启 Javascript 功能!

【読書メモ】The Art Of Unit Testing 2nd Edition - まとめ

1ヶ月ぶりにBlogを更新します。

前回では単体テストにおけるMockとStubについて特徴と使い方について説明しました。

単体テストに関して、これ以上コードを使って何か説明できる内容もないし、
テストフレームワークの使い方も公式ドキュメントを読めばいいだけです。

一応「The Art Of Unit Testing 2nd Edition」を完読したので、読書感想文ではないが、自分なりに得た知識を整理します。

単体テスト(Unit Test)とは何か

コードの最小単位(メソッド)に対してテストをすることである。改修によってプログラムが壊れていないことを担保し、コードの品質を維持する効果もある。

単体テストコードは原則開発者自身で実装する。自分で書いたコードに対するテストコードは当然自分で書く。テストコードを書くにはある程度の開発経験のが必要。単体テストのスコープが狭く、環境(DB,File,NetWork)に依存しないため、短時間で効率的にテストコードが書ける。

ただし、経験が浅い開発者にとって単体テストコードを書くには大量に時間を要する。
テストコード書く時間 > コード書く時間になるから、多くの開発者はそれで単体テストを諦める。

単体テスト(Unit Test)の価値

まず、一つ残酷な事実を伝えなければならない。

単体テストの目的は顧客の要件を検証することではなく、
日々の改修によってプログラムがぶっ壊れていないことを担保し、コードの品質を維持することである。

大事なことなので2回書きました(´ω`)

いあやいや、ちょっと待てよ。

単体テストで顧客の要件を検証できないとしたら、大量のテストコードを書いて自分を追い込むことになんの意味があるんだろう。

そう、この質問は最初に単体テストに触れた開発者が誰もが思うことである。

この質問に答える前に顧客の要件について少し語らせてください。

通常、顧客が要件を話すときときはこんな感じではないでしょうか。

「私は一覧表が欲しくて、項目は〇〇、☓☓、△△…があって、〇〇項目のフォーマット検証が必要で、データはDBに保存してくれ。そうそう、あとデータに重複チェックもよろしくな」

決して以下の感じで要件を言わないでしょう。

「こんなメソッドが欲しいんだ。HTTPリクエストはPOSTで、Idを引数にDBから対応するデータを取得し、Modelにセットして、そのModelをHTMLに紐づけて、ViewResultで返してくれ。そうそう、AuthorizeとValidationも当然つけてくれよ。」

プログラムが想定通りに動くことの担保

ソフトウェア開発は「テストシナリオ」が絡んでくると事態がややこしくなる。我々の開発は限られた期間ですべてのシナリオを網羅するテストコードを書くことが可能でしょうか。
それに、規模の大きいプロジェクトになってくると、開発者は全貌を把握することが難しく、自分の担当分のモジュールくらいしか詳細がわからない。

システムの全貌がわからないのに、どうやってすべてのシナリオをテストできるのでしょうか。

つまり、要件の検証以前に我々開発者はプログラムが自分の想定通りロジックで動くことを担保することに責任を持つ必要がある。

外部リソース2つ以上のメソッドの連携に依存した時点で単体テストではなく、統合テストやE2Eテストの範疇である。

品質の担保

また、顧客の要求は日々変わるもので、仕様変更を対応するために改修したらBugを発生させた、その発生させたBugを直したら、また新たしいBugが生まれた経験は誰でもあるだろう。

要件変更でプログラム改修する際に、その影響範囲を全部洗い出すことはとても困難である。
それに、顧客やPMはそんな分析時間を簡単に与えてくれない。

顧客に「こんな変更なんてちゃっちゃとやってくれ」と言われたこと誰でもあるでしょう^^;

そう、単体テストはプログラムの品質が低下していないことを担保するためのファイアウォールでもある。

例えば、ある日私が休暇を取った。運が悪いことにその日顧客から私のコードにBug報告があって、私の同僚はそのBugを直す担当になった。しかし、同僚は見当違いな修正をしてしまい、本来のBugと関係ない箇所を直してしまった。

そんなときに単体テストがあれば、私が帰った日にテストコードを実行し、同僚が直した部分のテストケースが失敗したことに気づき、コードをもとに戻して、本来のBugを直せば、コードが高品質のまま保つことができる。

このように、ちゃんとメンテナンスされているテストコードがあれば、改修で生まれたBugを素早く検知できる。

成功する単体テスト(Unit Test)の要素

信頼性( Trustworthiness )

  • テスト結果に自信がなければ、テストコードを書くことを継続できない
  • 「単体テスト」と「統合テスト」の違いをわからないと、そもそもテスト観点が間違い、テスト結果に自信持てなくなる
  • 何かの原因でテストが失敗したときに、直接コードorテストコードを直すのは良くない。まずは失敗原因を突き止めて、それを受け入れて初めてテスト結果の信頼に繋がる

メンテナンス性( Maintainability )

  • テストコードを日々メンテしてますか
  • デザインパターンでメンテナンス性を上げることができる
    • Repository Pattern, Service Pattern, Factory Pattern …etc
  • 良いアーキテクチャもメンテナンス性の向上を期待できる

可読性( Readability )

  • テストコードの命名で何をテストしようとしているのか判明できるか
  • テスト失敗時にメソッドの内容を見て原因を突き止められるか
  • 他の人がテストコードが読みにくければ、それらのコードを触ろうとしないし、メンテしようとは思わない。積み重ねると品質の悪化に繋がる

単体テスト(Unit Test)の注意事項

  • 単体テストは外部リソース(File I/O, Data Access, NetWork)や静的オブジェクト(static)に依存すべきではない
  • DAL(Data Access Layer)に対する単体テストは推奨しない、どうしてもやるなら個人的にOn Memory DBでやる
  • 単体テストは通常Setup Attributte と Teardown Attributeが使われない
  • 開発者ならテストコードの結果がすべてグリーンバーになるように行動すべき、言い訳は聞かん

コードの臭い (Code Smell)

  • テストメソッド間は一切の依存関係がない
    • あるテストメソッドが別のテストメソッドを呼んだら、テスト結果に影響する
  • テストメソッドに実行順序があるべきではない
    • テストメソッドA実行→テストメソッドB実行しなきゃならんなら、このコードは臭い
  • 1つのテストメソッドは同時に2つの検証をすべきではない

まとめ

久々に長文を書いたので、かなり疲れた。

やはり定期的に知識をOUTPUTしないと、定着しませんね。

出典

The Art of Unit Testing: with examples in C#