最近この開発方法を発見したので、私はそれがかなり良い方法論だと思っています。したがって、最初のプロジェクトでは、小さなDLLに相当するコード(C#.NETで、その価値があるもの)があり、このコードの一連のテストを作成したいのですが、その方法と方法について少し迷っています。どこから始めれば。
私はNUnitとVS2008を使用しています。どのような種類のクラスから始めるか、何のためにテストを書くか、そしてテストベースの開発にコードを移動する方法に関するヒントをいただければ幸いです。
MichaelFeathersの著書「WorkingEffectivelywithLegacyCode 」を参照してください。
要約すると、既存のコードをテスト可能でテスト済みのコードにリファクタリングするのは大変な作業です。時にはそれは実用的であるにはあまりにも多くの仕事です。これは、コードベースの大きさ、およびさまざまなクラスと関数が相互にどの程度依存しているかによって異なります。
テストなしでリファクタリングすると、動作に変化が生じます(つまりバグ)。そして、純粋主義者は、動作が変わらないことを確認するためのテストがないため、実際にはリファクタリングではないと言うでしょう。
アプリケーション全体に一度にテストを追加するのではなく、コードの領域で作業するときにテストを追加します。ほとんどの場合、これらの「ホットスポット」に再度戻る必要があります。
ボトムアップでテストを追加します。正確性について、小さな独立したクラスと関数をテストします。
上から下にテストを追加します。サブシステム全体をブラックボックスとしてテストし、コードの変更によって動作が変化するかどうかを確認します。そして、あなたはそれらをステップスルーして何が起こっているのかを知ることができます。このアプローチはおそらくあなたに最大の利益をもたらすでしょう。
テストを追加している間、最初は「正しい」動作が何であるかをあまり気にしないでください。動作の変化を検出して回避するようにしてください。テストされていない大規模なシステムでは、内部動作が正しくないように見えることがよくありますが、システムの他の部分はそれに依存しています。
データベース、ファイルシステム、ネットワークなどの依存関係を分離して、テスト中にモックデータプロバイダーと交換できるようにすることを検討してください。
プログラムに内部インターフェイス、つまり1つのサブシステム/レイヤーと別のサブシステム/レイヤーの間の境界を定義する行がない場合は、これらを導入してテストする必要があります。
また、RhinomocksやMoqなどの自動モックフレームワークは、ここで既存のクラスをモックするのに役立つ場合があります。テスト容易性のために設計されたコードでは、それらの必要性を実際には見つけていません。
私はそれを「テスト駆動リバースエンジニアリング」と呼んでいます。
「一番下」から始めてください。各クラスを個別に調べて、それ用にテストを作成できます。疑わしいときは、推測してください。
通常のTDDを順方向に実行しているときは、テストを神聖なものとして扱い、コードがおそらく壊れていると想定します。テストが間違っていることもありますが、あなたの出発点はそれがコードであるということです。
TDRE を実行している場合、コードに長期にわたるバグがあることを証明できるまでは、コードは神聖なものです。逆のケースでは、コードの周りにテストを書き、テストが機能するまでテストを微調整し、コードが機能すると主張します。
次に、悪いコードを掘り下げることができます。一部の悪いケードには、適切なテスト ケースがあります。これは、クリーンアップする必要があるだけです。ただし、一部の悪いコードには、無意味なテスト ケースも含まれます。これは、修正できる可能性のあるバグまたは不器用な設計である可能性があります。
コードが実際に間違っているかどうかを判断するには、最初から全体的なテスト ケースから始める必要もあります。実際に動くライブデータがスタートです。また、既知のバグのいずれかを生成するライブ データも、開始するのに適した場所です。
ライブ データを単体テスト ケースに変換する小さなコード ジェネレーターを作成しました。そうすれば、テストとリファクタリングのための一貫した基盤を得ることができます。
レガシーコードを効果的に使用することは、テストなしでコードを単体テスト環境に移行することに関しては私の聖書です。また、コードをテストしやすくする理由とテスト方法について多くの洞察を提供します。
また、例によるテスト駆動開発と実用的な単体テスト:NUnitを使用したC#でのテスト駆動開発は、その環境での単体テストの適切な入門書であることがわかりました。
TDDを開始するための簡単なアプローチの1つは、この日から最初にテストの作成を開始し、既存の(単体テストされていない)コードに触れる必要があるときはいつでも、変更する前にシステムの既存の動作を検証する合格テストを作成することです。これにより、後でこれらのテストを再実行して、何も壊れていないという自信を高めることができます。
テスト可能なコードは、付随するテストによって簡単に見つけることができます。いくつかある場合は、テスト可能である必要があります。ない場合は、反対のことを想定してください。;)
つまり、テスト駆動開発(TDD)は、設計戦略であるほどテスト戦略ではありません。最初に作成するテストは、クラスのインターフェースを設計するのに役立つだけでなく、クラス(またはそのことについてはサブシステム)のスコープを正しく取得するのにも役立ちます。
TDD中に作成したテストを後で実行すると、優れたテストになりますが、それはその設計哲学の(非常に歓迎される)副作用にすぎません。
そうは言っても、テストされることに対してあなたのコードからいくらかの抵抗を期待してください。簡単にテストできるように、コードを聞いてインターフェイスを変更してください。テストを書き始めるときに、おそらくそれを再設計するでしょう。
DLL は何らかのサービスを提供します。すべてのサービスについて、このサービスを取得する前に何をする必要がありますか?このサービスを取得するために渡す必要があるパラメーターは何ですか?要求されたサービスが正しく実行されたことをどのように知ることができますか?
これらの質問に対する答えが得られたら、最初のテストを作成できます。このようなテストは、ユニット テストよりもむしろキャラクタライゼーション テストと呼ばれますが、DLL が TDD を使用して開発されていない場合は、おそらくユニット テストよりも簡単に記述できます。
キャラクタライゼーション テストについては、M. Feathers の「レガシー コードを効果的に使用する」でも説明されており、他の回答で推奨されています。
また、新しいコード行を追加する前に、必ず失敗するテストを作成してください。