TDD談義への反応に対する雑感(テスト駆動開発を取り巻く誤解等)

 先日、twitter上でTDDに関する談義があったのだけれど、気になったのがそれに対するテストや品質の方々の反応。特にTDDの戒めである「品質保証を目的としていない」という書き込みに対してネガティブな反応が多かったのが気になった。
 開発経験もあり定義や概念の扱いに注意深い方々なので誤解の可能性はないと思うが、結構問題が入り組んでいるように感じたので、今回テストエンジニアと開発者の視点の差異を焦点にして一部の論点を整理したいと思う。

開発者のいう品質保証の定義

 まずTDD談義で開発者が「品質保証のためのテスト」「品質管理のためのテスト」などと呼んでいるテストの定義は、乱れや不統一感も多少あるけど、基本的にKent Beckや和田さんが使われているQAテストの定義によるもの(http://gihyo.jp/dev/serial/01/tdd/0003)。
 この定義で「品質保証のための単体テスト」といえば、かなり大雑把だが例えば仕様ベースで設計した単体テストや、独立性がある程度確保されている担当者が作った単体テストみたいなものも含まれる。そのため一般的に使われる品質保証(品質要件を基準以上満たしていることを保証する)の意味とは違って、単なるバグだしのテストでもこの定義下では品質保証のためのテストと見なされることもある。
 なお一般的な品質保証と用例が違うので、ここでは誤解を避けるために、「TDDでの単体テスト」をTDDのテストと呼び、「テストエンジニアの方が一般的に連想するような、テストレベルで規定されプログラムユニットを検証するために設計された単体テスト(TDD談義の文脈で使われるQAのテスト)」を一般的な単体テストと呼ぶ。少なくともTDDのテストという意味合いで単体テストという単語を使うのはここでは避けたい。

「品質保証を目的としていない」の意味

 取り合えずTDDでは、記述するテストコードは最低限に抑えるべきであり、テスト・QA工程で行うような大きめなテスト設計(少なくないテストケースを一度に実装したり、ユニット分析→設計→実装ときちんとステップを踏んだり、など)は行うべきでないとされる。
 その理由は単純に実装作業の効率を落とさないようにするため。最初に小規模でないテストコードを書いてしまうと、TDDの大きなメリットであるDefect Localization(実装ミスやバグがどこにあるか明示する)や細かいサイクルでのリファクタリングの効果を損なってしまい、実装を相対的に非効率なものにしてしまう。

 一応、一部では実装量の見積のためユニットテストの全体像を先に書き出してしまうこともあるけど、その対象はモジュールインターフェースといったものに限られることが多いし、実施も慎重に行われる。
 例えばテストコードの設計パターンの大著であるxUnit Test Patternsでは、テストコードをスケルトンのみにし、フレームワークのignore設定やイエローバー等の機能で有効動作するテストを絞り込むといった工夫で、まとめて書きだす場合でもTDDを損なわないようにすることを推奨している。


 そこで本題だけど、そうした「軽快にテストコードを書かないとTDDのメリットを殺してしまう」という背景の存在から、「TDDの方法論としての注意事項」として、TDDでは最初から一般的な単体テストを書くべきではない、と注意がよくされるようになっている。TDD談義で何度も出てくる「TDDは品質保証を目的としない」「TDDで品質保証を考えるべきでない」「TDDで品質は扱わない方がよい」というアドバイスも、基本的にこの注意と同類のものだろう(「QAを考えるべきでない」でなく、「テスト・QA工程で一般的に見られるような軽快でないテストと混同すべきでない」という文脈)。しつこく何度もいわれているのは、TDDに詳しくない方があまりにもこの注意を理解していないことによる。
 
 なお留意点として、こうした注意はTDDで蓄積・整理したテストを、品質保証も含む一般的な単体テストとして再構築・流用することを禁止するものではない。その点上記の注意には表現的に問題もあるかもしれない。
 またうまく運用すればテストファーストで大きなテストを書いても成果を出せるようになるかもしれないが、成果を出せるからといってそれがTDDになるとは限らない点にも注意がいる。TDD談義でテストファーストには2種類あるという言及があったように、そういった手法はTDDとは異なるテストファースト手法、と区別した方が混乱が少ないように感じる。

「TDDは他のテストと異なる」の意味

 TDDと一般的な単体テストが異なると言及される理由はいろいろあるけど、最も大きなものとしては、TDDはコーディングを進めるための手段であり、テスト設計のみを目的としていない点が挙げられると思う。

 TDDでは、実装にあわせて細かな粒度、細かなリズムでテストコードを継ぎ足し、既存のテストコードも製品コードに併せて柔軟に整理していく。そしてその度に自分が直近で行った実装作業のフィードバックを得ることを目指す。すなわちTDDのテストは、実装者に継続的で絶え間ないフィードバックを与えることを重要な目的としており、最終的に網羅性・整合性に優れた単体テストを出力することばかりを目的としているわけではない。
 「TDDでのテストは目的ではなく手段」「TDDでは品質保証を目的としない」という言及も、そのような特徴の延長線上にある言及だと思う。

 もちろんTDDで出力されるテストコードを、CIのテスト、一般的な単体テストとして効果的に再構築・運用することも大事だが、そうしたステップはTDDの一部分の要素でしかない(場合によってはTDDに含まれないとされることもある)。
 そのため少なくとも細かく継続的に実装のフィードバックを得るプロセスを考慮せず、最終的に得られるテストコードのみを見てTDDを論じるのは間違いだ。例えば単なる一般的な単体テストの前倒し設計はTDDとは言えない。


 またTDDと一般的な単体テストが異なると言及される他の理由として、TDDのテストの記述は実装担当者でないと手に負えないことも挙げられると思う。
 Kent Beckテスト駆動開発入門の前半を読んでもらえば分かると思うけど、TDDのテストは粒度が極めて細かく、変更(継ぎ足しも含む)も頻繁に行われる。さらにテストのために製品コードを書きかえるようなことも軽快に行われる。そうしたテストの実装作業が製品コードの実装作業と対になって連動する特長から、どうしても実装担当者の手が不可欠な領域、逆に独立したテスト担当者が手を出せない領域が、少なからず存在する。すなわち実装作業と不可分の領域があり、TDD=テスト手法として断言しにくいという背景もあるということだ。

TDDを取り巻く誤解と思われる反応、誤解と思われない反応

 とりあえずTDDに対して一般的にありがちな反応をいくつか列挙したいと思う。

「TDDと単体テスト設計は同類」

 具体的には「TDDと単体テスト設計は似たようなもの」「TDDがテスト手法でないと主張するのは、違いを極端に出そうとしているだけ」「TDDがテスト設計と違うと主張するのは、単に担当の違いによる」といった反応。

 こういった言及は誤解を含んでいる可能性があると感じる。例えばTDDは細かく継続的に実装上のフィードバックを得る手段であり、単体テスト設計手法として単体テストを出力するだけがTDDの目的でない、という前提を理解していない可能性があると感じる。

「開発者とテスト担当者が対立しているのでは」

 これもTDD談義に対する反応とすると、誤解の可能性が一部あるかもしれない。
 注意として、先のTDD談義では、あくまでTDDと単体テスト設計の相性が悪いという("「品質保証を目的としていない」の意味"で示した)注意には言及しても、開発者とテスト担当者を分離すべきといった主張はほとんどされていない。それでもなお対立しているように感じるのは「TDDと単体テスト設計は同類」で挙げた誤解が結構関わっているかもしれない。

(一応、TDD談義では開発者とテストエンジニアを分けるべき、開発者や品質に関わるなと具体的に言及するつぶやきもあるが、ごく少数なノイズなので今回は除外する)

「TDDを行う開発者とテストエンジニアは協力すべきだ」

 これはもっともな指摘。
 TDDを実践すると結果的に多くのテストコードが出力される上、製品コードの単体テスト容易性がかなり大きく向上する。そのためもし一般的な単体テストを実施している環境ならば、協力により高い相乗効果が得られると思う(これに関しては機会があればエントリにまとめたい)。

 ただ協力すべきとの主張が、「開発者とテスト担当者が対立している」といった意見の延長線上にあるのならば誤解が含まれているかもしれない。

最後に

 テスト設計の前倒しというのは聞こえが良いけど実際は色々と工夫しないと割に合わないことが多い。
 その中でTDDは割に合うように実装手法として最適化していった結果生まれた具体的な手法であるため、一般的なテスト手法と比べ異質な特徴を持っている。なので例えTDDをテスト手法と分類するにしても、あくまで「実装手法と密接に結びついたテスト手法」であることを見逃してはいけない。学ぶ際にも、実際にコーディングを進める視点がないと、誤解する可能性が高いんじゃないかと思う。

 なお今回は開発者側への誤解について扱ったけど、開発者側でもテストエンジニア側の用語や手法への不理解が根深く存在するように感じる。当然、開発、品質、テストが協調しあうのはとても大事なことだけれど、それだけにこういったロールをまたがる話題では相手の世界を尊重する視点が欠けてはならないと思う。自省も含むが、特に自分のロールや目的にとらわれて物事を解釈していないか、自分の無知を相手の無知と取り違えていないか、といった配慮は、十分に行う必要があると感じる。