web-dev-qa-db-ja.com

TDDと生産性

現在のプロジェクト(ゲーム、C++)では、開発中にテスト駆動開発を100%使用することにしました。

コードの品質に関しては、これは素晴らしいことです。私のコードは、これほどうまく設計されていたり、バグがなかったりすることはありません。 1年前にプロジェクトの開始時に記述したコードを見るときに、うんざりすることはありません。また、物事を構造化する方法について、テストが容易になるだけでなく、実装と使用がより簡単になることについて、はるかによく理解できました。 。

しかし...プロジェクトを開始してから1年になります。確かに、私は自分の暇なときにしか作業できませんが、TDDは以前に比べてかなり遅くなっています。開発の速度が遅いほど時間が経つにつれて改善することを読んだので、テストは以前よりずっと簡単に考えられるようになりましたが、今は1年経っていて、カタツムリのペースで作業しています。

作業が必要な次のステップを考えるたびに、私は毎回立ち止まって、実際のコードを記述できるように、そのテストをどのように書くかを考えなければなりません。何時間も行き詰まって、作成するコードを正確に把握しているが、完全にテストでカバーするために細かく分解する方法がわからない。それ以外の場合は、私はすぐに1ダースのテストを考え、1時間かけてテストを作成し、そうでなければ数分で作成できた実際のコードのごく一部をカバーします。

または、50番目のテストを終了して、ゲーム内の特定のエンティティと、その作成と使用のすべての側面をカバーした後、私はto-doリストを見て、コード化される次のエンティティを確認し、執筆の思考に恐怖を感じます実装するための別の50の同様のテスト。

去年の進捗を見て、「いまいましいプロジェクトを終わらせる」ためにTDDをやめることを検討しているところまで来ています。ただし、付属のコード品質をあきらめることは、私が期待していることではありません。テストの作成をやめると、コードをモジュール化してテスト可能なものにする習慣から抜け出します。

私はおそらくこれでとても遅いために何か間違ったことをしていますか?メリットを完全に失うことなく生産性を向上させる代替策はありますか? TAD?テストカバレッジが少ない?他の人々はどのようにしてすべての生産性とモチベーションを殺すことなくTDDを生き残るのですか?

136
Nairou

まず、あなたの経験を共有してくれたことに感謝し、懸念を表明することから始めましょう。これは珍しいことではありません。

  • 時間/生産性:テストを作成することは、テストを作成しないことよりも遅くなります。スコープをその範囲にすると、同意します。ただし、TDD以外のアプローチを適用する並行作業を実行する場合は、既存のコードの中断、検出、デバッグ、および修正に費やす時間が、ネガティブになる可能性があります。私にとって、TDDは、コードの信頼性を損なうことなく、私ができる最速の方法です。メソッドに何か付加価値のないものが見つかった場合は、それらを排除してください。
  • テストの数:N個をコーディングする場合、N個をテストする必要があります。ケントベックの行の1つを言い換える "機能させる場合にのみテストします。"
  • 何時間も行き詰まっている:私もそうです(ラインを停止する20分以上前のこともあります)。コードがデザインに作業が必要であることを伝えているだけです。テストはSUTクラスのもう1つのクライアントです。 テストでタイプの使用が困難であることが判明した場合、本番クライアントでも同様です。
  • 同様のテスト退屈:これは私が反論を書くためにもう少しコンテキストが必要です。とは言っても、立ち止まって類似点について考えてください。 これらのテストを何らかの方法でデータ駆動できますか?基本タイプに対してテストを作成することは可能ですか?次に、各派生に対して同じテストのセットを実行するだけです。テストを聞いてください。適切な種類の怠惰になって、退屈を回避する方法を理解できるかどうかを確認してください。
  • 次に何をする必要があるか(テスト/仕様)を考えるのをやめることは悪いことではありません。逆に、「正しいもの」を構築することをお勧めします。通常、それをテストする方法を考えることができない場合、私は通常、実装についても考えることができません。あなたがそこに着くまで実装のアイデアを空白にしておくのは良い考えです。たぶん、よりシンプルなソリューションはYAGNI風のプリエンプティブなデザインに隠れています。

そして、それは私に最終的なクエリをもたらします:どのように私はより良くなるのですか?私(またはAn)の答えは読む、反映する、実践するです。

例えば最近、私はタブを付け続けています

  • 私のリズムがRG [Ref] RG [Ref] RG [Ref]を反映しているか、それともRRRRGRRefか。
  • 赤/コンパイルエラー状態で費やされた時間の割合。
  • Red/Brokenビルド状態でスタックしていますか?
78
Gishu

100%のテストカバレッジは必要ありません。実用的です。

34
Alistair

TDDはまだ私をかなり遅くしています

それは実際には誤りです。

TDDがなければ、ほとんどが機能するコードの作成に数週間を費やし、翌年に多くの(すべてではない)バグを「テスト」して修正します。

TDDを使用すると、実際に機能するコードを1年かけて作成できます。次に、数週間の最終的な統合テストを行います。

経過時間はおそらく同じになるでしょう。 TDDソフトウェアの品質は大幅に向上します。

23
S.Lott

または、50番目のテストを終了して、ゲーム内の特定のエンティティと、その作成と使用のすべての側面をカバーした後、私はto-doリストを見て、コード化される次のエンティティを確認し、執筆の思考に恐怖を感じます実装するための別の50の同様のテスト。

これは、TDDの「リファクタリング」ステップをどれだけフォローしているのか不思議に思います。

すべてのテストに合格したら、今度はコードをリファクタリングして重複を削除します。人々は通常、これを覚えていますが、重複を取り除き、物事を単純化するために、これがテストをリファクタリングするときでもあることを忘れることがあります。

コードの再利用を可能にするために1つにマージする2つのエンティティがある場合は、それらのテストもマージすることを検討してください。実際にテストする必要があるのは、コード内でインクリメンタルな差異だけです。テストのメンテナンスを定期的に行わないと、テストがすぐに扱いにくくなる可能性があります。

役立つかもしれないTDDに関するいくつかの哲学的ポイント:

  • テストの作成経験は豊富ですが、テストの書き方がわからない場合は、間違いなくコードのにおいです。コードにモジュール性が不足しているため、小さく単純なテストの作成が難しくなっています。
  • TDDを使用する場合は、コードの一部をスパイクアウトすることは完全に許容されます。必要なコードを記述して、どのように見えるかを理解し、コードを削除して、テストを開始します。
  • エクササイズの一形態として、非常に厳しいTDDを実践していると思います。最初に始めるときは、必ず最初に必ず最初にテストを記述し、次に進む前にテストに合格するように最も単純なコードを記述してください。ただし、練習に慣れたら、これは必要ありません。記述した可能なすべてのコードパスの単体テストはありませんが、経験を通じて、単体テストでテストする必要があるもの、および機能テストや統合テストでカバーできるものを選択できます。代わりに。 TDDを1年間厳密に実践している場合は、この点にも近いと思います。

編集:単体テストの哲学のトピックに関して、これを読むのは興味深いかもしれません: The Way of Testivus

さらに、必ずしも実用的ではないにしても、より実用的なポイントは次のとおりです。

  • 開発言語としてC++について言及している。 JUnitやMockitoなどの優れたライブラリーを使用して、JavaでTDDを幅広く練習しました。ただし、ライブラリ(特に、モックフレームワーク)が利用できないため、C++のTDDは非常にイライラすることがわかりました。この点は現在の状況ではあまり役に立ちませんが、TDDを完全に廃止する前に考慮してください。
20
jaustin

非常に興味深い質問です。

注意すべき重要な点は、C++はテストがそれほど容易ではなく、一般にゲームもTDDの非常に悪い候補です。 OpenGL/DirectXが三角形をドライバーXで赤に、ドライバーYで黄色を描くかどうかを簡単にテストすることはできません。シェーダーの変換後にバンプマップの法線ベクトルが反転しない場合。また、精度の異なるドライバーバージョンでクリッピングの問題をテストすることもできません。不正な呼び出しによる未定義の描画動作も、手元にある正確なコードレビューとSDKでのみテストできます。音も悪い候補です。ゲームでも非常に重要なマルチスレッドは、単体テストではほとんど役に立ちません。だから厳しいです。

基本的にゲームは多くのGUI、サウンド、スレッドです。 GUIは、WM_を送信できる標準コンポーネントがあっても、単体テストは困難です。

したがって、テストできるのは、モデルロードクラス、テクスチャロードクラス、マトリックスライブラリなどです。これはコードが多くなく、最初のプロジェクトだけの場合は非常に頻繁に再利用できません。また、独自の形式にパックされているため、変更ツールなどをリリースしない限り、サードパーティの入力が大きく異なる可能性はほとんどありません。

繰り返しになりますが、私はTDDの第一人者でも伝道者でもないので、すべてを一粒の塩で味わってください。

おそらく、主要なコアコンポーネント(たとえば、マトリックスライブラリ、イメージライブラリ)のいくつかのテストを記述します。すべての関数の予期しない入力に一連のabort()を追加します。そして最も重要なことは、簡単に壊れない耐性/回復力のあるコードに集中することです。

1つのエラーについては、C++、RAII、および優れたデザインの巧妙な使用は、それらを防ぐための長い道のりです。

基本的に、ゲームをリリースしたい場合は、基本をカバーするためにやらなければならないことがたくさんあります。 TDDが役立つかどうかはわかりません。

10
Coder

他の答えにも同意しますが、非常に重要なポイントを追加します。リファクタリングのコストです!!

適切に記述された単体テストを使用すると、コードを安全に書き直すことができます。まず、適切に記述された単体テストは、コードの目的に関する優れたドキュメントを提供します。第二に、リファクタリングの不幸な副作用は既存のテストスイートに捕捉されます。したがって、古いコードの前提が新しいコードにも当てはまることが保証されます。

6
Morten

他の人々はどのようにしてすべての生産性とモチベーションを殺すことなくTDDを生き残るのですか?

これは私の経験とはまったく異なります。あなたは驚くほどインテリジェントで、バグのないコード(たとえば、1つのエラーでオフ)を書くか、コードにプログラムの動作を妨げるバグがあることに気付かず、実際には完了していません。

TDDは、あなた(そして私!)が間違いを犯したことを謙虚に知ることです。

私にとって、ユニットテストを書く時間は、最初からTDDを使用して行われるプロジェクトのデバッグ時間を短縮することで節約される以上のものです。

間違いをしない限り、TDDは私にとってそれほど重要ではありません。

4
Tom

ほんの少しの発言があります:

  1. テストしようとしているようですすべて。特定のコード/メソッドのリスクが高い場合やEdgeの場合のみ、おそらくすべきではありません。 80/20のルールがここに当てはまると確信しています。コードの最後の20%またはカバーされていないケースのテストを書くのに80%費やしています。

  2. 優先する。アジャイルソフトウェア開発に参加し、1か月でリリースするために本当に必要なことのリストを作成します。その後、リリースします。これにより、機能の優先順位について考えることができます。ええ、あなたのキャラクターがバク転をすることができればそれはクールですが、それはビジネス価値を持っていますか?

TDDは優れていますが、100%のテストカバレッジを目指していない場合に限り、実際のビジネスバリュー(つまり、機能、ゲームに何かを追加するもの)を生み出せない場合はそうではありません。

3
Cthulhu

はい、テストとコードの記述は、コードの記述よりも時間がかかる場合がありますが、コードと関連する単体テストの記述(TDDを使用)は、コードを記述してデバッグするよりもはるかに予測可能です。

TDDを使用すると、デバッグがほとんどなくなります。これにより、すべての開発プロセスがはるかに予測可能になり、最終的には間違いなく高速になります。

継続的なリファクタリング-包括的な単体テストスイートなしでは、深刻なリファクタリングを行うことは不可能です。ユニットテストベースのセーフティネットを構築する最も効率的な方法は、TDD中です。適切にリファクタリングされたコードは、コードを保守する設計者/チームの全体的な生産性を大幅に向上させます。

1
ratkok

ゲームの範囲を狭めて、誰かがプレイしたりリリースしたりできるようにすることを検討してください。ゲームをリリースするのにあまりにも長く待たずにテスト標準を維持することは、モチベーションを維持するための中立的な立場になるかもしれません。ユーザーからのフィードバックは長期的にメリットをもたらす可能性があり、テストによって追加や変更に慣れることができます。

0
JeffO