4つのルールと5つのコツでチラ見するテスト駆動開発入門 ~本を読んでTDDを実践したまとめ
2011/06/04
巷で噂のケント・ベックの「テスト駆動開発入門」読みました。めっっっちゃ良かったので、今日はその内容と実践してみた事なんかをずらずらずらずら書いていきます。独断と偏見に基づいてまとめていくよ。
めっっっちゃ良い本なのでTDDに興味のある人は全員買うが吉です。写経して手を動かしながら学べるこの内容で3150円は破格。
※言い回しが複雑な事があって、人によってはケントベックの文章がちょっと読みづらく感じるかもしれませんが、内容は確かです。
テストコードの書き方のルール4つ
「テスト駆動開発入門」を読んで一番響いた&実際に役に立ったテストコードの書き方たちを、4つのルールにまとめてみました。
- 1. 無駄なテストコードは書かない
- 何をテストすべきかについて、ケントベックは以下の4つをテストすればよいと答えています。(p190)
- 条件分岐
- ループ
- 操作(データクラスのメソッド呼び出しとか)
- 多相性(ポリモーフィズム)
- 2. 壊れにくく、独立したテストをつくる
- xUnitであればsetUp()やtearDown()メソッドを活用しながらテストコードを書く。
- 壊れにくいこと - 「予想外に失敗するテストは、アプリケーションの一部が予想外の方法で別の部分に影響していることを示す」(p190)
- DBや各種設定ファイルの内容、サーバやネットワークの状態によらずにテストが期待通りに成功するようにテストコードを書きましょう。
- 独立していること - 「テストの実行は、テスト間で決して影響すべきではない。」(p123)
- 例えば、テストスイート全体を実行しなくても、一部のテストメソッドだけ抽出して実行してもテストが期待通りに成功するようにテストコードを書くこと。
- 3. メソッド名は後から読みやすいものを
- テストコードに書いたメソッド名は、自分で呼び出して利用することはない(テストスイートが実行される時だけ使われるだけ)。なので例えば、テストメソッド名は超長くなってもOK。これはコメントやドキュメントの記述量削減にもつながる。
class MyTest extends PHPUnit_Framework_TestCase { // こういうメソッド名でも良いけど public function testGetUserName(){ $this->assertEquals( "tarou", GetUserName(1)); } // 少し長くてもちゃんと説明してる方が分かりやすい public function testGetUserName_Invalid_null(){ $this->assertEquals( "tarou", GetUserName(null)); } // ちなみにPHPUnitなら、使いたければメソッド名に日本語を使って書いても大丈夫 public function testGetUserName_正常系_100を入力(){ $this->assertEquals( "tarou", GetUserName(100)); } }
- 4. assertは具体的に
- 良い例悪い例を書いておきます。
class MyTest extends PHPUnit_Framework_TestCase { // 悪い例:null以外を返せばテストを満たしてしまう public function testArea_Bad(){ $this->assertTrue(rectangle->area() != 0); } // 良い例:具体的な期待値を指定している public function testArea_Better(){ $this->assertTrue(rectangle->area() == 50); } }
実際にテストコードを書く上でのコツ5つ
異論や個人差もあると思うけど、このおかげでテストコードがグンと書きやすくなったコツを経験談を交えながら書いておきます。
- 1. テストリスト作りは効果高い
- ケントベックが本でかなり丁寧に解説しているテストリストですが、実際作ってみると効率的にテストコードを書けました。リストの書き方は自由ですが、ケントベックは実装する必要のある操作の例やリファクタリングを書くべきと指南しています。僕の場合は、操作の例を引数つきで書くことが多いです。やってみた効果の感想としては、
- ・頭が整理される
- 頭が整理されて無駄な手戻りが少なくなります
- ・集中力を持続しやすくなる
- 作業中に別観点でのテストが思いついても、リストに書き残しておけるので安心して作業に戻れる
- ・進み具合が見える
- 実装が終わったテストコードの項目を二重線で消したりすると進捗を可視化できる
- 2. モチベーションが命
- テストファーストで開発していて、うっかり自分で書いたコードでハマって抜けるまで時間がかかったりすると、いろいろいたたまれない気分になることがあります。というか、よくなります。自分のスキルを疑い始めたり、お客さんに申し訳ない気分になったりと、これがけっこうつらい。そうなることを全力で避けるための3つのポイント。
- ・レッド(テストコードの記述)、グリーン(コードを実装)、リファクタリング(重複したコードを取り除いていく)のリズム感が全て
- 自分で書いたテストコードでハマってもヘコむことがあると腹をくくっておくこと。
- ・テストスイートの実行に時間がかかりすぎないように
- テストスイートの実行に10分以上かかっていないことを確認する。実行に長時間要するテストは頻繁には実行されなくなり、しばらく実行しないと、おそらく動作しなくなる。(p190)
- ・無駄な時間は使わない
- 例えば失敗することがわかっているなら、作成したテストを実行しなくてもよい(p122)
- 3. テストコードは可読性がクソ重要
- コードは書くよりも読まれるケースの方が多い。常に読みやすいコードを心がけること。読みやすい最小限のコード行数は3行(p160)
- 4. 一つのテストメソッドの中でのassertは少なめに
- 一つのテストメソッド内で呼び出すassertの数は、
経験で言うと通常1、2個、最大でも4~5個程度におさえます。1つがベスト(@t_wada談)。理由は、2つ以上assertを書くと、エラーが発生した場合にその後のassert文が実行されず、いくつのassertが通っていないか、一回のxUnitの実行で見通しが悪くなってしまうため。なお、assertを複数書く場合は、それぞれのassertの目的が重複していないことを確認した上で追加する。 - 5. 既存のコード資産にTDDを導入するときは心してかかる
- 既に動いているソフトウェアのソースコードの中身がいかに汚くても、実際に動作しているので正義という考え方があります。いろんな面で正論なのでこれはこれで良いと思うけど、中長期的に運用していくソフトウェアであれば技術的負債を早期に返済していくことのメリットもやっぱり大きいですよね。既存のコードにTDDを導入することについてケントベックは、「難問で、執筆すれば一冊の本になる。」(p196)と前置きをしながらも、スコープを絞って、劇的にシンプルにしたくなる衝動を抑えながら部分的にTDDを導入していくべき、とアドバイスしています。
TDDの更なる普及のためにいろいろな疑問に答えます(ケントベックが)
いわゆるFAQコーナー。後々役に立つことになりそうなtipsたちを本文中から拾い集めました。
- Q:自動テストの使用をどのように普及させればよいか
- A:テストを用いた説明を求め、提供すべき。面と向かってTDDを押し付けることはしない。(p134)
- Q:欠陥が報告されたらどうすればよいか
- A:失敗する最小のテストを作成し、実行した後で修正する。回帰テストは、完全な先見があれば、最初のコーディング時に作成していたテストである。回帰テストを作成するたびに、どうすれば、最初にそのテストを作成できていたかを考えよう。(p136)
- Q:一日の作業の終え方は?
- A:チームで開発する場合、一日の終わりにはグリーンの状態で終える。(でないと、次の日の始めに別の人がビルドできなくなる)また、一人で開発する場合、最後のテストを失敗のままにして終えることで、次の日に作業を再開するときの手がかりとすることができる。(p146, 147)
- Q:いつテストを削除すべきか。
- A:削除しても自信が持てること、他のテストが同じストーリーを表現できていることの2点を満たせているなら、削除してよいとのこと(p194)
言語別のノウハウも必要
上記では書ききれなかったのですが、例えばPHPでテストコードを書くときには変数の型を意識して書く必要があったりと、言語別に必要なノウハウもあります。来月のTDD Boot Camp in Tokyo #tddbcではその辺りの事とかTDDの普及についてとかをLTのテーマにさせてもらうことになりそうです。
本で紹介されていたケントベックのテストコードを素早く書くための戦略(仮実装、三角測量、明白な実装)をやってみた感想なんかも、機会があれば書いていきたいなー。
Related Posts
about me
@remore is a software engineer, weekend contrabassist, and occasional public speaker. Read more