業務でテスト駆動開発(TDD)を実践するようになってから約 2 か月が経ち、現在のチームでの開発スタイルにも慣れてきました。
TDD の実践のため、ケント・ベック氏の「テスト駆動開発」を読んだところ、TDD 初心者が知るべき内容が多いと感じたので、私のような方に共有できればと思います。
テストの粒度
TDD を行っていると、テスト実装の際このような疑問が浮かんでくる。
・各々のテストがどのくらいをカバーすべきか
・リファクタリングの家庭で中継地点をどのくらい作るか
ロジックに一行加えることや、少量のリファクタリングを行う際にテストを書くべきか?
ロジックに数百行加えたり、数時間かかるリファクタリングを行う際にテストを書くべきか?
この答えは、「どちらもできるようになろう」だ。
小さいステップで行うのがテスト駆動開発者の傾向ではあるが、アプリケーションレベルのテストのみで駆動する人もいるようだ。
リファクタリングを行う際には、ステップはたくさんの小さいステップに分割することを心掛ける。小さいステップのリファクタリングを複数回繰り返すことで間違いの入りにくいリファクタリングを行える。
テストしなくてよいものはあるか
この質問には、以下がシンプルな答えと述べている。
不安が退屈に変わるまでテストを書く
具体的には、以下がテストすべき対象である。
・条件分岐
・ループ
・操作
・ポリモーフィズム
ただし、自分の書いたコードに限る(信頼できないと考える理由がある場合は除く)。
外部由来のバグによって、より多くのロジックを自分で書かなければならない場合、上記のリストをテスト項目として使う。
さらに慎重を期する場合、バグを記録するテスト(学習用テスト)を書いておく。
もしバグが修正された場合、そのテストが失敗して教えてくれる。
良いテストを見分けることができるか
アプリケーションの設計に問題を抱えている場合、テストには以下のような兆候が現れる。
・前準備に要するコードが長い
アサーションを行うテスト対象オブジェクトの準備に 100 行も必要なのであれば、オブジェクトが大きすぎるので、分割すべきだ。
・前準備コードの重複
共通の前準備コードを配置する場所がすぐに見つからない場合、互いに密に関連しあうオブジェクトが多すぎる事を示唆している。
・テスト実行時間が長い
TDD において、時間がかかるテストは実行の頻度が低くなる。そして長い間動かしていないテストは、おそらく通らなくなっている。長い実行時間は、アプリケーションの一部を切り出したテストが難しいことを示唆している。
・脆いテスト
思わぬタイミングで失敗するテストは、アプリケーションのどこかが意外な形で他の部分に影響している可能性を示唆している。離れた箇所からの影響を排除するように設計し直さなければならないかもしれない。
どのような時にテストを消すべきか
もし 2 つのテストの間に重複が発生した場合、2 つとも残しておくべきか?
2 つの基準で判断すべきだ。
・自信があるか?
テストを消すことでシステムの振る舞いに対する自信が減るのであれば、決して削除してはならない。
・同じシナリオと解釈できるか?
2 つのテストがコードの同じ部分を実行しているとしても、読み手から異なるシナリオと映るのであれば、消さないでおく。
巨大なシステムをテストできるか?
重複の除去された小さなオブジェクトで構成されるアプリケーションでは、機能の総量は TDD の有効性とは無関係である。
その理由は、システムの大きさに関係なく個々のオブジェクトが独立してテスト可能だからである。
アプリケーションレベルのテストで開発を駆動できるか?
小さなレベルのテストで開発を駆動する際に問題になるのは、顧客が望んでいる物を作れるかどうか?という点である。このリスクを排除するために、アプリケーションレベルのテストを導入すべきだ。
もちろん、アプリケーションレベルのテストを走らせるための技術的な問題、そしてチーム全体でテストを書くことを浸透させるという文化的な問題がある。
TDD は誰のためのものか?
TDD は、動くかわからないコードをコピペしてつなぎ合わせ、そのコードから目を背け平気な顔をしているような開発者のためのものではない。TDD は以下のような仮説で成り立っている。
「より良いコードを書けば、よりうまくいく」
これの意味するところは、正しいタイミングで正しい問いに気づける、より綺麗な設計になる、学びを得るにしたがってさらに設計を改善できる、といった事である。
プロジェクトを成功に導くという観点では、TDD はやりすぎとも言えるかもしれない。しかし欠陥数は大幅に減り、設計は明確になる。
エンジニアからしても、プロジェクトのコードを腐らせることなく、コード、システムに対する自信を持て、より多くの変更が可能になる。
なぜ TDD は機能するのか?
TDD が必ず欠陥を減少させるという根拠はないが、事例証拠は多数あり、副次的効果は明白だ。
TDD が設計判断のフィードバックループを短くするということも、優位性を示す証拠になりうる。
TDD は、正しいコードを「引き寄せる」ようなプログラミングプラクティスである。
全てのテストが通る状態で機能を一度に 1 つ追加する、という状況は、状態空間中の全ての軌道が収束する点(数学用語でいうアトラクター)を作り出していることに等しい。