書籍学習記録 - 「テスト駆動開発」
June 4, 2021
学習記録として、記憶に残った内容をメモしておきます。
「テスト駆動開」Kent Beck / 和田 卓人 #
テスト駆動開発を原点から学ぶ
本書は、自分たちのコードに自信を持って開発を続けたいプログラマ、チームリーダー向けに、テスト駆動開発(TDD)の実践方法を解説した“Test-Driven Development By Example"の日本語版です。テスト駆動開発の考案者である Kent Beck 自身によって書かれた原典を、日本におけるテスト駆動開発の第一人者である和田卓人氏が訳しました。 テスト駆動開発とは単にテスト自動化を行うことではなく、ユニットテストとリファクタリングを両輪とした小さいサイクルを回すことで不確実性を制御し、不断の設計進化を可能にする手法であることを、実例を通して学ぶことができます。
著者の Kent Beck 氏は「エクストリームプログラミング」の著者でもあることを知り驚きました。同書籍は数年前に読んだことがあるのですが、確かに文章の語りかけ方が似ている(というか同一人物なので同じというべきか)気がしました。
内容メモ #
本文から #
テスト駆動開発(TDD)のリズム
- まずはテストを1つ書く
- すべてのテストを走らせ、新しいテストの失敗を確認する
- 小さな変更を行う
- すべてのテストを走らせ、すべて成功することを確認する
- リファクタリングを行って重複を除去する
一般的な TDD のサイクルは次のようなものになる。
- テストを書く。頭の中で想像した操作がどんなコードとして現れるかを考える。いまあなたは物語を書いている。欲しいと思うインタフェースを創作しよう。物語には、正しい答えを導くために必要そうな要素をすべて盛り込もう。
- 動かす。テストが全て通り、バーがグリーンになる状態へと素早く到達する。きれいでシンプルな解が見えているなら、そのとおり書いてしまおう。もし実現に少し時間がかかりそうな場合は、いったん TODO リストに書いておき、目の前のバーを速やかにグリーンにする作業に戻る。なんでもいいからとにかく動かすという考え方は、優れたエンジニアリングのルールに反するように感じるし、特にベテランの開発者にとっては、審美的にも価値観的にも受け入れがたいかもしれない。それでも、素早いグリーンはすべての罪を赦す。ただし、赦されるのはほんの短い時間だけだ。
- 正しくする。システムは動く状態までは来たが、いろいろと汚い手を使ってしまった。悔い改め、ソフトウェアの正道を通り、書いてしまった重複を除去して、グリーンバーに素早く戻ろう。
目指すのは、動作するきれいなコードだ(この Ron Jeffries の簡潔な表現は素晴らしい)。動作するきれいなコードは、偉大なプログラマでもすぐには書けないことがあるし、普通のプログラマならなおさらだ(私もそうだ)。ここは分割統治しよう。最初に「動作する」に取り組み、その後で「きれいな」に取り組む。この方法はアーキテクチャ駆動とは正反対だ。アーキテクチャ駆動は「きれいな」に最初に取り組み、そのうえで苦心してあちこち設計の辻褄を合わせながらどうにか「動作する」を実現させる。
将来の読み手を考えたテスト
テストを書くのは、目の前のプログラミングを楽しくやりがいのあるものにするためだけでなく、いま考えていることを将来の仲間に伝えるロゼッタストーンの役割も担ってほしいからだ。だから、テストを書くときには読者のことを考えるのが何よりも大切だ。
テストしなくてよいものはあるか
シンプルに答えるなら、本書レビュアーの Phlip が言うところの「不安が退屈に変わるまでテストを書く」となる。
TDD におけるテストの考え方は実用主義に貫かれている。TDD においてテストは目的を達成するための手段であり、その目的とは、大いなる自信を伴うコードだ。たとえテスト無しでも実装の知識による自信があるのであれば、テストを書かない。
どのようなときにテストを消すべきか
テストの数は多いに越したことはないが、もし2つのテストの間に重複が発生した場合、2つとも残しておくべきだろうか。そこには2つの判断基準がある。
- 1つ目の判断基準は自信だ。テストを消すことでシステムの振る舞いに対する自信が減るのであれば、決して削除してはならない。
- 2つ目の判断基準はコミュニケーションだ。2つのテストがコードの同じ部分を実行しているとしても、テストの読み手には異なるシナリオと映るのであれば、消さずにそのままにしておこう。
つまり、もし2つのテストに自信の面でもコミュニケーションの面でも重複があるのであれば、役に立たない方を消そう。
訳者解説から #
TDD の T は「テスト」の一部に過ぎない
テスト駆動開発が耳目を集め、実践者が増えるに従って、TDD 実践者やソフトウェアテストの専門家の間から「テスト駆動開発のテストは、本当にテストだろうか」という問いが浮かび上がってきました。 「テスト」の古典的な定義を引用するなら、『ソフトウェア・テストの技法 第2版』のテストの定義が挙げられるでしょう。
テストとは、エラーをみつけるつもりでプログラムを実行する過程である。
ソフトウェアの世界における本来のテストとは、認知の外を探究する、いわば想像的破壊行為です。それに対して TDD のテストとは、いわばプログラミングや設計の補助線、治具です。 つまり、テスト駆動開発のテストは、ソフトウェアテストの世界の「テスト」の中では一部を占めるだけに過ぎません。にもかかわらず、テスト駆動開発を過大評価する人や、名前から受けるイメージをそのまま受け取ってしまう人などから「テスト駆動開発を行うからバグはゼロになる」「テスト駆動開発を行うからテストエンジニアは不要」といった誤った意見が聞かれるようになってきました。 テスト駆動開発のテストは、本当にテストだろうか。テストではないとしたら、それは何だろうか。という問いが現実の問題として現れてきました。
それは “Checking” である。
Michael Bolton と James Bach は、ブログエントリ “Testing vs. Checking” において、開発者自信が書く自動テストは、実際にはテストを行っているのではなく、自分が考えた通りに動くかどうかを確かめている、つまり高速でチェックしているのだと言う考えを述べました。
ソフトウェアテストの中で TDD が占めるポジション
テスト駆動開発のテストがテストであったとしても、それはソフトウェアテストのすべてを指しているわけではなさそうです。では広範なソフトウェアテストの中で、テスト駆動開発はどのような位置を占めるのでしょうか。 Brian Marick はいち早くこの問題を分析し、「テスト」を4つの象限(Business facing / Technology facing x Support Programming / Critique product)に分類するモデルを発表します。
(中略)
4象限のモデルを見てみると、ソフトウェアテストの世界を大きく2つに分けるならば、TDD や BDD の技術は常に「開発を促進し、チームを支援する」側にあることがわかります。「TDD を行っているからバグはない」や「TDD を行っているからテストエンジニアは不要」といった発言が誤っているのは、TDD には第三者テストのような批評の視点が欠けているからなのです。 「製品を批評する技術面のテスト」はセキュリティテストや負荷テストなどであり、「製品を批評するビジネス面のテスト」は探索的テストに代表されるテストエンジニアの高度な専門領域です。どちらの象限も、欠くべからざるテストであることは明白です。
道を照らす明かり
私たちが日々行なっている実際のプログラミングは、無策で挑むと際限なく複雑になり、次第にコントロール不能になっていきます。このため、私たちは対象となる世界をシンプルな部品に分解し、その組み合わせによって複雑さを軽減する方向に努力します。部品が手続きであれ、オブジェクトであれ、関数であれ、分解していく過程はまっすぐな一本道ではありません。複数の可能性を考えながら、試行錯誤を重ねてぼんやりとだけ見える道を進んでいきます。テスト駆動開発は、その道を照らす明かりのひとつだと考えてみてください。
テストは質を上げない - 質をあげるのはプログラミング
開発者自信が自分のコードにテストを書くプラクティスは、バージョン管理等と同様に、現代のソフトウェア開発者の基本スキルになりました。自分が書いたコードに対してテストコードを書く。これは現代では当たり前の光景です。 テスト駆動開発は、そこからさらに先へ進む際のヒントとなります。テストを書くことによってコードが良くならないと、結局は質は上がりません。テストするだけでは、質は上がりません。質がわかるだけです。質を上げるのはプログラミングです。 テストを書いても設計を改善しないのであれば、それはただの回帰テストであり、現状の追認でしかありません。テスト駆動開発における質の向上の手段は、リファクタリングによる継続的でインクリメンタルな設計であり、「単なるテストファースト」と「テスト駆動開発」の違いはそこにあります。 テスト駆動開発によって開発されてきたコードが、最初に考えていた設計からどのくらい変わったか。そこに驚きはあったか。それは良い変化だったか、観察してみてください。テストを書きながら開発することによって、設計が良い方向に変わり、コードが改善され続け、それによって自分自身が開発に前向きになること、それがテスト駆動開発の目指すゴールです。