テスト駆動設計を採用することで失うものは何ですか?
ネガのみをリストします。否定的な形で書かれた特典を記載しないでください。
「実際の」TDDを実行する場合(最初に赤、緑、リファクタリングの手順でテストする)、統合ポイントをテストする場合は、モック/スタブの使用も開始する必要があります。
モックの使用を開始するときは、しばらくすると、依存性注入(DI)と制御の反転(IoC)コンテナーの使用を開始する必要があります。そのためには、すべてにインターフェースを使用する必要があります(それ自体に多くの落とし穴があります)。
結局のところ、単に「昔ながらの方法」で行うよりも、はるかに多くのコードを作成する必要があります。顧客クラスだけでなく、インターフェース、モッククラス、いくつかのIoC構成、およびいくつかのテストも作成する必要があります。
また、テストコードも維持および管理する必要があることを忘れないでください。テストは他のすべてと同じように読みやすくする必要があり、優れたコードを書くには時間がかかります。
多くの開発者は、これらすべての「正しい方法」を実行する方法を完全には理解していません。しかし、誰もがTDDがソフトウェアを開発する唯一の真の方法であると彼らに言うので、彼らはできる限り最善を尽くします。
思ったよりずっと難しいです。多くの場合、TDDで行われるプロジェクトは、誰も実際に理解していない多くのコードで終わることがあります。ユニットテストはしばしば間違ったこと、間違った方法をテストします。そして、いわゆる教祖でさえも、良いテストがどのように見えるべきかについては誰も同意しません。
これらのテストはすべて、システムの動作を「変更」すること(リファクタリングとは反対)を非常に難しくし、単純な変更は非常に困難で時間がかかるようになります。
TDDの文献を読むと、非常に優れた例が常にいくつかありますが、実際のアプリケーションでは、多くの場合、ユーザーインターフェイスとデータベースが必要です。これはTDDが非常に困難になる場所であり、ほとんどのソースは良い答えを提供していません。もしそうなら、それは常により多くの抽象化を含みます:モックオブジェクト、インターフェースへのプログラミング、MVC / MVPパターンなど、これもまた多くの知識を必要とします、そして...あなたはさらに多くのコードを書かなければなりません。
ですから注意してください...熱心なチームと、優れたテストの書き方を知っていて、優れたアーキテクチャについていくつかのことを知っている経験豊富な開発者が少なくとも1人いない場合は、TDDの道を進む前によく考えなければなりません。 。
いくつかの欠点があります (特にプロジェクトの基礎を書く場合は特に、利点がないと主張しているわけではありません。最終的には多くの時間を節約できます):
テストの数が多くなると、システムを変更すると、変更によって無効になったテストに応じて、一部またはすべてのテストを書き直す必要がある場合があります。これにより、比較的迅速な変更が非常に時間のかかる変更になる可能性があります。
また、実際に優れた設計原則ではなく、TDD に基づいて設計上の決定を下し始める可能性もあります。TDD が要求する方法をテストすることが不可能な、非常に単純で簡単なソリューションを持っていたかもしれませんが、実際には間違いを起こしやすいはるかに複雑なシステムになっています。
私にとって最大の問題は、「それに入る」のにかかる膨大な時間の損失だと思います。私はまだ TDD の旅の始まりにいます (興味がある場合は、テストの冒険の最新情報については私のブログを参照してください)。
脳を「テストモード」にするには長い時間がかかり、「テスト可能なコード」を書くこと自体がスキルです。
TBH さん、プライベート メソッドを公開することに関するJason Cohen のコメントには敬意を表して同意しません。新しい作業方法では、以前よりも public メソッドを作成していません。ただし、アーキテクチャの変更が必要であり、コードのモジュールを「ホットプラグ」して、他のすべてを簡単にテストできるようにする必要があります。これを行うために、コードの内部をよりアクセスしやすくするべきではありません。そうでなければ、すべてが公開された振り出しに戻ってしまいます。そのカプセル化はどこにあるのでしょうか?
つまり、(IMO)一言で言えば:
PS: ポジティブへのリンクが必要な場合は、いくつかの質問をして回答しました。プロフィールをチェックしてください。
私がテスト駆動開発を実践してきた数年間で、最大の欠点は次のとおりです。
TDD はペアで行うのが最適です。1つには、 if/elseステートメントの書き方を知っていると、単に実装を書きたいという衝動に抵抗するのは困難です。しかし、あなたが彼を仕事に就かせているので、ペアはあなたを仕事に保ちます. 残念なことに、多くの企業/管理者は、これがリソースの有効な使用法であるとは考えていません。同時に実行する必要がある 2 つの機能があるのに、なぜ 2 人で 1 つの機能を作成するのにお金を払う必要があるのでしょうか?
一部の人々は、単体テストを書くための忍耐力を持っていません。自分の仕事に誇りを持っている人もいます。または、複雑なメソッド/関数が画面の端からにじみ出るのを見るのが好きな人もいます。TDD は万人向けではありませんが、そうであってほしいと心から願っています。コードを継承する貧しい人々にとって、物事の維持が非常に簡単になります。
理想的には、コードの決定を誤った場合にのみテストが壊れます。つまり、システムが一方向に機能すると思っていたのに、そうではなかったことが判明したということです。テスト、または (小さな) テストのセットを破ることによって、これは実際には朗報です。新しいコードがシステムに与える影響を正確に把握できます。ただし、テストが適切に作成されていないか、密結合されているか、さらに悪いことに生成されている場合 (咳VS テスト)、テストを維持することはすぐに合唱団になる可能性があります。そして、十分な数のテストが、それらが作成している認識された価値よりも多くの作業を引き起こし始めた後、スケジュールが圧縮されたときにテストが最初に削除されます (たとえば、クランチ タイムになる)。
繰り返しになりますが、この方法論を順守する場合、コードはデフォルトで 100% テストされます。通常、コード カバレッジは 90% 以上になると考えられます。これは通常、いくつかのテンプレート スタイル アーキテクチャがあり、ベースがテストされているときに発生し、テンプレートのカスタマイズをテストしないように手抜きをしようとします。また、以前に遭遇したことのない新しい障壁に遭遇すると、それをテストする際に学習曲線があることがわかりました. コードのいくつかの行を昔ながらの方法で書いていることは認めますが、それを 100% 維持したいと思っています。(私は学校でやり過ぎだったと思います、エルスクール)。
ただし、TDD の利点は、アプリケーションをカバーし、1 つの変更ですべてが壊れるほど脆弱ではない一連の優れたテストを実現できれば、次のような単純なアイデアのマイナス面をはるかに上回っていると言えます。プロジェクトの 1 日目と同じように、プロジェクトの 300 日目にも新しい機能を追加し続けることができます。これは、TDD をバグだらけのすべてのコードに対する魔法の弾丸だと考えて TDD を試しているすべての人に起こるわけではありません。うまくいかない、ピリオド。
個人的には、TDD を使用すると、より単純なコードを記述できるようになり、特定のコード ソリューションが機能するかどうかを議論する時間が減り、米国政府が定める基準を満たさないコード行を変更することを恐れないことがわかりましたチーム。
TDD はマスターするのが難しい分野であり、私はそれを数年間行っていますが、今でも常に新しいテスト手法を学んでいます。前もって膨大な時間を投資しますが、長期的には、自動化された単体テストを使用しない場合よりも持続可能性が大幅に向上します。さて、私の上司だけがこれを理解できればいいのですが。
最初のTDDプロジェクトには、時間と個人の自由という2つの大きな損失があります。
あなたは次の理由で時間を失います:
あなたは個人の自由を失います:
お役に立てれば
TDD では、これらのテストに合格するコードを記述する前に、クラスがどのように動作するかを計画する必要があります。これはプラスでもあり、マイナスでもあります。
コードが書かれる前の「真空」でテストを書くのは難しいと思います。私の経験では、最初のテストを書いているときに忘れていたクラスを書いているときに必然的に何かを考えるたびに、テストにつまずく傾向があります。次に、クラスをリファクタリングするだけでなく、テストもリファクタリングします。これを 3 ~ 4 回繰り返すと、イライラすることがあります。
私は、最初にクラスのドラフトを作成してから、一連の単体テストを作成 (および維持) することを好みます。ドラフトを作成した後は、TDD がうまく機能します。たとえば、バグが報告された場合は、そのバグを悪用するテストを作成し、テストに合格するようにコードを修正します。
TDD ではプロトタイピングが非常に困難になる可能性があります。解決に向けてどのような道をたどるかわからない場合は、事前にテストを作成することが困難になる可能性があります (非常に広範なものを除く)。これは苦痛になる可能性があります。
正直なところ、大多数のプロジェクトの「コア開発」には、本当のマイナス面があるとは思いません。通常、自分のコードがテストを必要としないほど十分に優れていると信じている人々 (決してそうではありません) や、単純にそれらを書くのに煩わされない人々によって、本来よりもはるかに多くのことが語られています。
さて、このストレッチでは、テストをデバッグする必要があります。また、テストの作成にはある程度のコストがかかりますが、ほとんどの人は、デバッグの時間を節約し、安定性を確保するために、アプリケーションの存続期間にわたって報われる先行投資であることに同意します。
ただし、私が個人的に抱えていた最大の問題は、実際にテストを作成するための規律を確立することです。チーム、特に確立されたチームでは、費やした時間に価値があると納得させるのは難しい場合があります。
TDDの欠点は、通常、「アジャイル」手法と密接に関連していることです。これは、システムの文書化を重要視せず、テストが「開発者のみに存在する他の値ではなく、特定の値を返す必要がある」理由の背後にある理解です。頭。
開発者が、テストが1つの特定の値を返し、他の値を返さない理由を離れるか忘れるとすぐに、あなたは失敗します。TDDは、適切に文書化され、人間が読める形式の(つまり、先のとがった髪のマネージャー)文書に囲まれていれば問題ありません。この文書は、世界が変化し、アプリも必要になる5年後に参照できます。
私がドキュメントについて話すとき、これはコードの宣伝文句ではありません。これは、アプリケーションの外部に存在する公式の記述です。たとえば、マネージャー、弁護士、更新する必要のある貧しい樹液が参照できるユースケースや背景情報などです。 2011年のあなたのコード。
私は、TDD が私を夢中にさせるいくつかの状況に遭遇しました。いくつか名前を付けるには:
テスト ケースの保守性:
大企業に所属している場合、多くの場合、自分でテスト ケースを作成する必要はありません。または、少なくともテスト ケースのほとんどは、入社時に他の誰かによって作成されます。アプリケーションの機能はときどき変更されます。HP Quality Center など、それらを追跡するためのシステムが整っていなければ、すぐに気が狂ってしまうでしょう。
これはまた、新しいチーム メンバーがテスト ケースで何が起こっているかを把握するのにかなりの時間がかかることを意味します。次に、これは必要なより多くのお金に変換できます。
テスト自動化の複雑さ:
テスト ケースの一部またはすべてを機械実行可能なテスト スクリプトに自動化する場合、これらのテスト スクリプトが対応する手動テスト ケースと同期し、アプリケーションの変更に沿っていることを確認する必要があります。
また、バグの発見に役立つコードのデバッグにも時間を費やします。私の意見では、これらのバグのほとんどは、テスト チームがアプリケーションの変更を自動化テスト スクリプトに反映できていないことに起因しています。ビジネス ロジック、GUI、およびその他の内部要素の変更により、スクリプトの実行が停止したり、信頼性が低下したりする可能性があります。変化が非常に微妙で、検出が難しい場合があります。テーブル 1 の情報に基づいて計算したため、テーブル 1 がテーブル 2 になったため、すべてのスクリプトが失敗を報告したことがあります (誰かがアプリケーション コードでテーブル オブジェクトの名前を交換したため)。
テストが十分に行われていない場合、テストに合格したからといって、「すべてが機能する」という誤った感覚に陥る可能性があります。理論的には、テストに合格した場合、コードは機能しています。しかし、最初から完全にコードを書くことができれば、テストは必要ありません。ここでの教訓は、何かを完了と呼ぶ前に、自分でサニティ チェックを行うようにすることです。テストだけに頼るのではありません。
健全性チェックでテストされていないものが見つかった場合は、戻ってそのテストを作成してください。
最大の問題は、適切な単体テストの書き方を知らない人々です。それらは相互に依存するテストを作成します(そしてそれらはAntでうまく実行されますが、Eclipseから実行すると、順序が異なるという理由だけで突然失敗します)。彼らは特に何もテストしないテストを書きます-彼らはただコードをデバッグし、結果をチェックし、そしてそれを「test1」と呼んでテストに変更します。それらの単体テストを記述しやすくなるという理由だけで、クラスとメソッドの範囲が広がります。単体テストのコードはひどいものであり、すべての古典的なプログラミングの問題(重い結合、500行の長さのメソッド、ハードコードされた値、コードの重複)があり、維持するのは大変です。いくつかの奇妙な理由で、人々はユニットテストを「実際の」コードより劣ったものとして扱います、そして彼らはしません それらの品質はまったく気にしません。:-(
テストの作成に費やす多くの時間を失います。もちろん、これはバグをより早くキャッチすることで、プロジェクトの終わりまでに救われるかもしれません。
最大の欠点は、本当に TDD を適切に実行したい場合、成功するまでに多くの失敗を繰り返さなければならないことです。いくつのソフトウェア会社が働いているか (KLOC あたりのドル) を考えると、最終的にはクビになるでしょう。コードがより速く、よりクリーンで、保守が容易で、バグが少ない場合でも。
KLOC (または実装された要件 - テストされていなくても) によって支払われる会社で働いている場合は、TDD (またはコード レビュー、ペア プログラミング、継続的インテグレーションなど) から離れてください。
困難で予期せぬ要件に再び焦点を当てることは、プログラマーの絶え間ない悩みの種です。テスト駆動型開発では、既知のありふれた要件に集中する必要があり、開発を既に想像されているものに制限します。
考えてみてください。最終的に特定のテスト ケースに合わせて設計することになる可能性が高いため、創造性を発揮して「ユーザーが X、Y、Z を実行できたらかっこいいだろう」と考え始めることはありません。したがって、そのユーザーが潜在的なクールな要件 X、Y、および Z に興奮し始めると、設計がすでに指定されたテスト ケースに厳密に集中しすぎて、調整が困難になる可能性があります。
もちろん、これはもろ刃の剣です。ユーザーが望む可能性のある考えられる、想像できるあらゆる X、Y、Z の設計にすべての時間を費やすと、必然的に何も完成しません。あなたが何かを完成させた場合、あなたがコード/デザインで何をしているのか、誰にも (あなた自身を含めて) 知ることは不可能になります。
最初の開発時間については、2 番目に回答します。また、テストの安全性がなければ快適に作業する能力も失います。私はTDDのナッツバーとも言われているので、友達を何人か失う可能性があります;)
より遅いと認識されます。長期的には、それが将来あなたを救うという悲しみの点では真実ではありませんが、最終的にはより多くのコードを書くことになるので、間違いなく「コーディングではなくテスト」に時間を費やしています. それは欠陥のある議論ですが、あなたは尋ねました!
慣れるまでには少し時間がかかり、プロジェクトでそれを開始するには少し時間がかかりますが...自動テストで非常に迅速に発見できたはずのばかげたバグを見つけたとき、テスト駆動アプローチを行わなかったことをいつも後悔しています。さらに、TDD はコードの品質を向上させます。
アプリケーションを別の方法で作成する必要があります。つまり、アプリケーションをテスト可能にする方法です。最初はこれがどれほど難しいか驚くでしょう。
書く前に何を書くかを考えるという概念が難しすぎると感じる人もいます。モッキングなどの概念も難しい場合があります。レガシ アプリの TDD は、テスト用に設計されていない場合、非常に困難になる可能性があります。TDD フレンドリーではないフレームワークを中心とした TDD も苦労する可能性があります。
TDD はスキルであるため、ジュニア開発者は最初は苦労するかもしれません (主に、この方法で作業するように教えられていないためです)。
全体的には、人々が熟練するにつれて短所は解決され、最終的に「臭い」コードを抽象化し、より安定したシステムを手に入れることになります。
XML フィードやデータベースなどの「ランダム」データのテストを作成するのは難しく、時間がかかります (それほど難しくありません)。私は最近、気象データ フィードの作業に時間を費やしています。少なくともTDDの経験があまりないので、そのためのテストを書くのはかなり混乱しています。
複数の責任を持つ大規模なクラスを失うことになります。また、複数の責任を持つ大きなメソッドを失う可能性があります。リファクタリングの能力がいくらか失われるかもしれませんが、リファクタリングの必要性もいくらか失われます。
Jason Cohen は次のように述べています。TDD には、コードに特定の編成が必要です。これは構造的に間違っている可能性があります。たとえば、プライベート メソッドはクラスの外で呼び出すことができないため、メソッドを非プライベートにしてテスト可能にする必要があります。
これは、抽象化の失敗を示していると言えます。プライベート コードを本当にテストする必要がある場合は、おそらく別のクラスにする必要があります。
デイブ・マン
テストが常に最新であることを確認する必要があります。赤信号を無視し始めた瞬間は、テストが無意味になる瞬間です。
また、テストが包括的であることを確認する必要があります。そうしないと、大きなバグが発生した瞬間に、最終的にコードを書くのに時間を費やせるようになった息苦しい管理タイプが文句を言うでしょう。
私のチームにアジャイル開発を教えた人は計画を信じていませんでした。
彼のモットーは、リファクタリング、リファクタリング、リファクタリングでした。リファクタリングとは「前もって計画しない」ことを意味することを理解するようになりました。
BDD の原則を TDD プロジェクトに適用すると、ここに挙げた主な欠点 (混乱、誤解など) のいくつかを軽減できることを付け加えておきます。BDD に慣れていない場合は、Dan North の紹介を読む必要があります。彼は、職場で TDD を適用することから生じたいくつかの問題に対する答えとして、この概念を思いつきました。Dan による BDD の紹介は、ここにあります。
私がこの提案をするのは、BDD がこれらの欠点のいくつかに対処し、ギャップ ストップとして機能するためです。フィードバックを収集する際には、この点を考慮する必要があります。
TDD では、コードに特定の編成が必要です。これは非効率的であるか、読みにくい可能性があります。または、アーキテクチャ的にも間違っています。たとえば、private
メソッドはクラスの外で呼び出すことができないため、メソッドを非公開にしてテスト可能にする必要がありますが、これは間違っています。
コードが変更されると、テストも変更する必要があります。リファクタリングでは、これは多くの余分な作業になる可能性があります。
開発時間の増加: すべてのメソッドをテストする必要があり、依存関係のある大規模なアプリケーションがある場合は、テスト用にデータを準備してクリーンアップする必要があります。
段階的な変更 (コードのリファクタリング) を行う能力を失いますが、それでもコードが本来の目的を果たしていることに、あたたかく、ぼんやりと感じます。明示的な依存関係を最小限に抑えてコードを構築するという実質的に自由で痛みのない動機を失います。IOW、気付かないうちに多くの依存関係を埋め込むことができます。TDD を使用すると、テストを作成するときに依存関係が痛みや匂いとして現れます。