テスト駆動開発(TDD)はもう終わっているのか? Part 1

本エントリは翻訳リクエストより投稿いただきました。
ありがとうございます!リクエストまだまだお待ちしております!

後編を公開しました(2014/10/8)

これは、 テスト駆動開発(TDD)とTDDがソフトウェア設計に与える影響について Kent BeckDavid Heinemeier Hansson、および著者の3人で行った一連のディスカッションの議事録です。

ディスカッションに至った経緯

あるセンセーショナルな発言とブログ記事が発端となり、お互いの見解と経験について理解を深める目的で、話し合いが持たれました。

この会話のきっかけとなったのは、DavidがRailsConfで行った基調演説です。彼はRailsコミュニティでTDDおよびユニットテストへの不満を表明しました。程なくして、彼はいくつかのブログ記事を公開しましたが、そのうちの最初の記事で“TDDは終わった”と宣言したのです。

それから2~3日後、Davidのその後の記事について私がタイプミスの修正を送ったところ、Davidは彼の発言やブログに対する私の感想を聞きたいと言ってくれました。そこで私たちは、1時間ほどSkypeで楽しく有意義な議論をしました。DavidはKentとも似たような議論を行い、Kentが私たち3人でこの議論を発展させ、公開しようと提案しました。その後Davidがこのアイデアをツィートすると、多くの好意的な反応が寄せられたというわけです。

私たちが一連のディスカッションについて決めたのは、1回30分というアバウトなルールだけで、他にプランはありませんでした。このディスカッションの主な目的は、私たちがお互いの経験や考え方について学ぶことで、それを公開したのは、私たちの動画を見たがる人たちが大勢いるに違いないというナルシスト的な思いからです。質問は最終回の前まで受け付けていませんでしたが、最終回では2~3の質問を受けました。私たちが質問を取り上げる間に、ブログやTwitterでも議論が展開されました。

今回の件では、ThoughtWorks、特にWesley Clockに感謝しています。ビデオの設置からスケジュールの管理まで裏方のサポートのほとんどを仕切ってくれました。また、動画を見るよりテキストを読む方がいいという人たちのため、私は毎回ディスカッションの終了後に議事録を作成することにしました。

1:TDDと自信

2014年5月9日

私たちはTDDのフローにまつわるそれぞれの経験および、TDDとセルフテスティングコードの定義があいまいであることについて議論しました。

議事録

まず、Davidが彼の思うTDDとユニットテストの主な問題点を3つ挙げました。その3つとは、TDDとユニットテストの定義が混同していること、モックでアーキテクチャを動かすことによって生じるテストに伴う弊害、そして、TDDのレッド/グリーン/リファクタリングのサイクルが彼にとって全く役に立たない理由です。そこで私が、TDDなどの開発手法が誕生した背景を理解するため、TDDの歴史を知るべきだと発言すると、KentがTDDの由来を説明してくれました。彼はSmalltalkを使っていろいろと試行錯誤することから始め、TDDが自分の性に合うと気づいたそうです。

私たちがC3プロジェクトで初めて一緒に仕事をした時は、まだTDDを使っていませんでしたが、プログラミングごとにコードとテストを一緒に行うようにしていたと私は発言しました。するとKentは、プログラマは自分のコードが正しいという自信を持つべきだし、TDDはそこに到達するための(唯一ではなく)1つの手法だと言いました。Davidはプログラマの幸せを追求するRubyの設計目標を気に入っているし、テストなくしてプログラミングは終わらないという考え方にも賛同していますが、TDDをそのための手法とすることは好みません。人はそれぞれ異なる頭脳を持っているように、好みの手法や言語も違うというのが彼の意見で、セルフテスティングコードから得られる確信とTDDが一緒にされていることが気に入らないのです。

Kentは最近のFacebookのハッカソンで、半分程度はTDDが適用できたが、残りの半分ではTDDは不適切だったと話しました。TDDが適用可能なコードでは楽しく作業できたものの、そうでないパートはやりにくかったそうです。しかし、TDDが不適切なパートでも、Kentはリグレッションテストや短いフィードバックループを活用しました。彼にとって2つのやり方をミックスすることは何の問題もなく、例えるなら、クラシックとジャズの両方を演奏するようなものです。TDDは、彼が学校で数学を学んだ頃のことを思い出させてくれると言います。つまり、常に例題が必要だということです。

Davidの場合は、TDDがうまくいく状況を経験したことはあるものの、彼の仕事のほとんどのケースではうまくいかないと言います。そんな彼の疑問は、TDDを機能させることに伴う犠牲は何かということです。多くのプログラマが下手なトレードオフをしています。中でも、大量のモックは深刻です。一方Kentは、それはトレードオフ次第だと考えます。つまり、中間結果をテスト対象とする価値があるか考えようということです。彼はコンパイラの例を挙げ、コンパイラで実行される中間構文木がテストポイントとして適切な場合は、設計としてもより優れていると説明しました。しかし、モックに関するDavidの質問に対して、Kentはめったにモックを使用しないと回答した上で、モックをよく使う人たちはリファクタリングを難しいと捉えるが、自分はテストのおかげでリファクタリングが楽になると思うと話しました。

私が指摘したのは、1つの用語に異なる解釈が混在することによって生じる2つの問題点です。1つ目は、DavidによるTDDの批判がTDDでは大量のモックに頼らざるを得ないという憶測に基づいており、それは正しくないという点、そして2つ目は、セルフテスティングコードとTDDは違うという点です。TDDはセルフテスティングコードを実現する1つの手法にすぎません。これに対しDavidは、彼が問題にしているのは、モックを偏重するTDDを正当化し、その結果、独立したユニットテストを実現するために下手な設計のコードを大量に書いてしまう人たちがいることだと話しました。

私はTime Copをするからと言ってディスカッションを打ち切り、次回はTDDに起因する弊害について掘り下げようと提案しました。それは本当に弊害と言えるのか、また、どうやってその判断を下すのかについて議論したいと思います。

参考資料

2:テストがもたらす設計の弊害

2014年5月16日

Davidは、TDDを使うことで、過剰な間接参照という複雑な構造によって生じるテスト起因の設計の弊害が生じると考えています。この例としては、Railsのヘキサゴナルアーキテクチャのような手法が挙げられます。一方Kentは、TDDというよりは設計の決定の質に弊害の原因があると考えます。

今回のディスカッションの前にDavidが作成した要点を参照するとよいでしょう。彼が懸念する設計の弊害の例が紹介されています。また、惜しまれながらこの世を去ったJim Weirichがヘキサゴナルアーキテクチャへの取り組みについて語った動画もお勧めです。

議事録

今回のディスカッションは、私の質問から始まりました。TDDは設計の弊害を引き起こすのか。結果として生じる弊害は、本当に弊害と言えるのか。設計に弊害があるかどうかをどのように判断するのか。Davidは自分が以前公開した要点について説明しました。その要点とは、TDDで大量のモックを使った結果、たどり着くアーキテクチャの一例を示したものです。アプリケーションの各レイヤが分離しており、例えば実際のモデルやデータベース、またはリクエスト/レスポンスサイクルと絡むことなく、コントローラをテストすることができます。Davidの懸念は、その例自体というよりも、テストを独立して行いやすくするために不必要な間接参照と複雑な構造が求められるという点にあります。

Kentは、テストによる設計の弊害をTDDのせいにするのは、変な場所に車で行ったことを車のせいにするようなものだと言います。Davidの提示した設計の原因はTDDではないとして、このような間接参照が良い技法になる状況もあるため、間接参照すべきかどうかを見極めることが大切だとKentは言います。Davidはこれに同意せず、いったんTDDという馬(もしくは車)に飛び乗ったら、ある一定の方法に導かれる、つまり一度に広範囲をカバーする巨大なテストになると主張しました。するとKentは、むしろTDDでは一度に1回ずつ設計を決定するのだと言って反論しました。TDDは段階的に設計を進化させていきます。そして、テストでカバーされる範囲の粒度は、人によって好みが分かれます。

KentはDavidに、彼が紹介した要点の中で、例えばどんなことが構造を複雑化させる原因になっていると思うのか尋ねました(「設計が気になるのは変更を加える時であって、存在するだけなら誰も気にしない」)。これに対してDavidは、コードのサイズとコード変更の難易度には直接的な相関関係があり、間接参照による影響は避けられないと言いました。つまり、10行のコードは60行のコードより分かりやすく、変更しやすいということです。間接参照が増えればレイヤが増え、コストが高くなります。続けてDavidは、TDDのレッド/グリーン/リファクタリングのフローは常習性が高いと指摘し(ここでKentが、自分はこの世で最も貧しいクスリの売人だとジョークを飛ばしました)、そのせいでプログラマがまずい決定を下す結果になっていると言いました。私はこの意見に反論して、それはTDDのせいではなく、外部から独立させるため、つまり、環境(この場合はRails)から切り離すというヘキサゴナルアーキテクチャの本質に起因するものだと述べました。

Davidは、独立させる必要性が生じるのはTDDのせいだと述べ、これまで独立させることに関するさまざまな議論を聞いてきたが、理にかなっているのはテストに関する議論だけだと言いました。彼によると、Railsのアプリケーションをコマンドラインアプリケーションにしたいと思うような人はめったにいないし、それはばかげた発想です。それと同じように、呼び出しのためにインメモリストアをWebサービスにスワップアウトすることもできないと彼は言います。なぜなら、両者の運用上の特性は異なるからです。真の目標は、両者をスワップできるようにすることではなく、独立したテストです。Kentは、インメモリとWebサービスでは失敗のケースが異なるため、同様に扱うことはできない(「分離できたと思っても、必ずどこかでつながっている」)として、Davidの意見に賛成しました。要素間の境界を完全に切り離すことはできません。「問題は、要素間を分離するのにどれだけの時間を費やすかということです」

Kentは10行のコードと60行のコードの違いを凝集度の問題と捉えており、Davidもそれに同意しました。しかし、Davidは凝集度と結合度は相入れない場合が多いと主張して、大抵は凝集度を高めることに価値が置かれ結合度の高さが犠牲になっていると指摘しました。Kentは、外部依存を排除する方法は他にもあると述べ、コンパイラで中間結果を使用できることを挙げました。何かテストしにくいものが出てきた時は、新しい設計の発想が必要だというサインであり、テストしやすい優れた設計に至る発想を求めている時は、立ち上がって散歩するのも有効だと言いました。Davidは、テストのおかげでより優れた設計ができることもあると認めたものの、彼の経験では反対の結果になることが多く、テストしやすい優れた設計はなかったと言いました。Kentは、Davidの自信の低さを指摘して、今はその発想が見えないかもしれないが、見えるまでの間は努力しなければならないと叱咤しながらも、そのうち洞察することができるはずだと楽観視していました。Davidは、この考えを”自信に基づくTDD”だと一蹴しました。つまり、彼も以前はいずれ洞察できるはずだと感じていたものの、ありもしない理想的なソリューションを追い求める憂うつなループにはまり込んでしまったのです。Kentは自分がTDDについてではなく、ソフトウェア設計全般について話していたことを明らかにした上で、問題はTDDではなく、どうやってフィードバックを得るかだと言いました。ソフトウェア設計について考えることは重要で、優れた設計の発想があれば、必ず報われます。そのような発想を得るには、ワークフローではなく、労働と休息のタイミングを見極めたり、外部から刺激を受けたり、他者と協力したりすることが重要になるというのがKentの意見です。

次のトピックを決め、今回のディスカッションは終了しました。次回は、プログラミング中にフィードバックを得る際のトレードオフについて話します。

参考資料

3:フィードバックとQA

2014年5月20日

今回は、プログラミング中にフィードバックを得るためのさまざまな方法と、開発者にフィードバックを供給する品質保証(QA)の役割について議論しました。

議事録

今回のディスカッションは、TDDの決定とはトレードオフについて考えることだというKentの発言で始まりました。「理想の世界では、私たちがプログラミングで行う決定に対して、即座に絶対確実なフィードバックが得られる」(中略)「キーを叩いて、もしそのコードがデプロイできるものなら、すぐにデプロイされる」 しかしそれが現状では理想に過ぎない以上、その理想からどれくらい譲歩するかを考慮すべきだ。そう言ってKentは、トレードオフの制約をいくつか挙げました。

  • 頻度:どれくらいの頻度でフィードバックをもらいたいか?
  • 精度:レッド/グリーンのシグナルに求める精度はどれくらいか?
  • オーバーヘッド:どれくらいのコストを費やせるか?
  • 寿命:存続の見込みと期間の両方を考慮したソフトウェアの寿命はどれくらいか?

彼の考えによると、トレードオフでは、これら4つの項目を比較する必要があります。「この議論の目的はお互いの同意を得ることではない。自分の考えを明らかにして、それに対する建設的な批判をもらうことで、トレードオフの理解を深めるというのが私の個人的な目標だ」

私は、私たちが求める3つのフィードバックについてまとめました。

  • ソフトウェアがそのユーザにとって何か有益なことをしているか? この点においては、テストが有効な場合(給与計算など)とそうでない場合(HTMLレンダリングなど)がある。
  • 何も壊していないか? 「この場合は、セルフテスティングコードに…助けられる」 全てのテストで少なくとも一度は失敗した方がよい。
  • 自分のコードベースは健全か? 健全であればスピーディーに設計できる。そのコードを引き継ぐ人が誰か分からない場合は、コードベースの健全性を保つことがより難しくなる。

Davidは、TDDの成功がQAの軽視を招いたという話題を持ち出し、多くのビジネスでTDDが採用されてQA部門が排除されたと言いました。Basecampでも2~3年前まではQAを担う人材がいなかったと言います。Davidは、TDDがプログラマに「過剰な自信を抱かせ、QAは必要ないと思い込ませてしまった」と考えています。QAという古いモデルは崩壊し、代わりにTDDが席巻しました。Davidは「自分以外の誰かにテストさせない限りは、適切な品質を確保し、優れたソフトウェアを作成することはできないと思う」と述べ、自分はQAの担当者を迎え入れることがどれだけ有益なことか知っているため、今の状況を残念に思うと言いました。

Davidの提示したもう1つの問題は、トレードオフを理解するためには、コストを理解する必要があるということです。Davidによると、TDDはそのプラス面ばかりが注目されています。テストがもたらす弊害というものが存在することに気がつかないのは、コストを軽視しているせいだというのが彼の意見です。トレードオフとコストの関連性は、他のことにも当てはまります。ここでDavidは信頼性のコストを例に挙げました。信頼性を99%から99.999%に上げるコストは、99%まで上げるコストよりも飛躍的に高くなります。また、クリティカリティについても考慮しなくてはなりません。スペースシャトルやペースメーカーの場合は信頼性の高さが重要ですが、探索型Webサイトを作る場合は違います。テストなしには1行の本番用コードも書かないというルールは、クリティカリティにまつわるトレードオフには合わないとDavidは話しました。

Kentは再びQAの話題に戻って、QA部門との関係がうまく機能しなかった昔の経験に言及しました。彼のFacebookのオフィスにあるポスターには「Facebookで起こる問題は全て他人事ではない」と書かれており、Facebookはその規模の会社としては驚くほどその主義に忠実であるとKentは感じています。Facebookには最近までQAがなく、プログラマがその責務を果たしてきました。Kentは、「問題は比べる対象が何かによって変わる」と言います。有能なQA部隊がいる状況と比べたら、QAがないことはマイナスですが、昔のように機能しないQAを抱えるくらいなら、QAがない方がマシだという論理です。そこで私は、ThoughtWorksではほぼ全てのプロジェクトにQAがいると言いました。私の考えでは、90年代から大きく変化したことの中には、QAとの機能不全の敵対的な関係がなくなったことだけでなく、手動のスクリプトテストが排除されたことも含まれると思います。そしてそのおかげで、スタートアップはQAなしでも運営できるようになりました。Davidは、初期段階のスピードアップのために、注意しながらQAをトレードオフするのはよいとしても、プログラマによるテストが過剰評価されて探索的テストの価値が見えなくなる場合があると指摘しました。彼は、もし開発者がQAなしで十分な品質のソフトウェアを作成できると思っているなら、それは間違いだと言います。なぜなら、自分で行ったテストがグリーン(成功)だったとしても、本番環境でユーザがどんなことをするかまでは把握できないため、開発者の思いも寄らないようなことが起きる可能性があるからです。

Davidが最も深刻だと考える問題は、開発者がカスタマーサービスに関わっていない場合です。多くのプログラマはオンコールを敬遠します。なぜならそれが退屈で面倒な仕事だからです。しかし、オンコールはフィードバックループにつながります。Davidは、テストでグリーンの結果が出たコードに満足して、それ以上の段階に目を向けなくなる状況を心配しています。これに対しKentは、自分で行うテストの結果には限界があることを忘れないために、緑色(グリーン)のバーに赤色(レッド)のピクセルをいくつか置くべきだと述べて、「オンコールがフィードバックループにつながり、自分では書かなかったテストを発見できる」と言いました。Facebookのプログラマはオンコールを義務づけられており、誰もがそれに不満を持っているが、それを回避する方法はないとKentは言います。自分はミスをしないという考えは間違いで、そう思い込んだ途端に成長が止まってしまいます。結局のところ、「あなたはもう二度と失敗しないだろうと世間が大目に見てくれることはない」のだから、深夜2時の電話であらかじめミスを指摘されておく方が得策だというのがKentの意見です。

私はディスカッションの終わりで、テストに伴うコスト(Davidが提示した2番目の問題)について今回は話す時間がなかったため、次回に持ち越そうと提案しました。そして、全く同じトピックについて書かかれたMike Blandの記事が私のサイトに首尾よく公開されたことも伝えました。

後編を公開しました。