TDD について私が発見したことは、テストをセットアップするのに時間がかかり、自然に怠け者である私は常にできるだけ少ないコードを書きたいということです。私が最初に行うことは、コンストラクターがすべてのプロパティを設定したことをテストすることですが、これはやり過ぎですか?
私の質問は、単体テストをどのレベルの粒度で作成するのですか?
..そして、テストが多すぎる場合はありますか?
TDD について私が発見したことは、テストをセットアップするのに時間がかかり、自然に怠け者である私は常にできるだけ少ないコードを書きたいということです。私が最初に行うことは、コンストラクターがすべてのプロパティを設定したことをテストすることですが、これはやり過ぎですか?
私の質問は、単体テストをどのレベルの粒度で作成するのですか?
..そして、テストが多すぎる場合はありますか?
私はテストに対してではなく、機能するコードに対して報酬を受け取るので、私の哲学は、一定の信頼レベルに達するためにテストをできるだけ少なくすることです (このレベルの信頼は業界標準に比べて高いと思いますが、それは傲慢かもしれません)。 . 通常、ある種の間違い (コンストラクターで間違った変数を設定するなど) をしない場合は、テストしません。私はテスト エラーを理解する傾向があるので、複雑な条件付きのロジックがある場合は特に注意が必要です。チームでコーディングするときは、戦略を変更して、チーム全体で間違いがちなコードを慎重にテストします。
人によって、この哲学に基づいたさまざまなテスト戦略がありますが、テストがコーディングの内部ループに最適に適合する方法についての理解が未熟であることを考えると、それは妥当なことのように思えます。10 年か 20 年後には、どのテストを作成し、どのテストを作成しないか、およびその違いを見分ける方法について、より普遍的な理論が確立される可能性があります。その間、実験は順調に進んでいるようです。
壊れると予想されるものや、エッジ ケースの単体テストを作成します。その後、バグ レポートが届いたら、バグの修正を書き込む前に、テスト ケースを追加する必要があります。開発者は、次のことを確信できます。
添付のコメントによると、単体テストを作成するこのアプローチは、時間の経過とともに特定のクラスで多くのバグが発見された場合、問題を引き起こす可能性があると思います。これはおそらく、慎重さが役立つ場合です。再発する可能性が高いバグ、または再発すると深刻な問題が発生するバグに対してのみ単体テストを追加します。これらのシナリオでは、単体テストでの統合テストの測定が役立つことがわかりました。コードパスの上位にあるコードをテストすると、下位のコードパスをカバーできます。
すべてを可能な限り単純にする必要がありますが、単純にする必要はありません。- A.アインシュタイン
TDD について最も誤解されていることの 1 つは、その最初の単語です。テスト。それがBDDが登場した理由です。最初の D が重要な D 、つまり Driven であることを人々が本当に理解していなかったからです。私たちは皆、テストについて多かれ少なかれ考え、設計の推進については少なからず考える傾向があります。これはあなたの質問に対するあいまいな答えだと思いますが、実際にテストしているものではなく、コードを駆動する方法を検討する必要があります。これは、カバレッジツールが役立つものです。デザインはかなり大きく、より問題のある問題です。
「すべて」をテストすることを提案する人へ:メソッドを「完全にテスト」するにint square(int x)は、一般的な言語と典型的な環境で約 40 億のテスト ケースが必要であることを認識してください。
実際には、それよりもさらに悪いことです。メソッドは、他のメンバーの値を変更しないことvoid setX(int newX)も義務付けられています。xobj.yobj.zobj.setX(42);
「すべて」のサブセットをテストすることだけが現実的です。 これを受け入れると、信じられないほど基本的な動作をテストしないことを検討する方が受け入れやすくなります。すべてのプログラマーは、バグの場所の確率分布を持っています。賢明なアプローチは、バグの可能性が高いと推定される領域のテストにエネルギーを集中することです。
古典的な答えは、「壊れる可能性があるものは何でもテストする」です。これは、set または get 以外のことを何も行わない setter と getter をテストすることは、おそらくテストが多すぎて、時間をかける必要がないことを意味していると解釈します。あなたのIDEがあなたのためにそれらを書かない限り、あなたもそうかもしれません.
プロパティを設定していないコンストラクターが後でエラーにつながる可能性がある場合、それらが設定されていることをテストすることはやり過ぎではありません。
これから書くクラスの仮定をカバーするテストを書きます。テストは要件を強制します。基本的に、たとえば x が 3 にならない場合、その要件をカバーするテストがあることを確認します。
条件をカバーするテストを書かないと、後で「人間による」テストで問題が発生します。その時はもちろん書きますが、早めにキャッチしたいです。要点は、テストは (おそらく) 退屈ですが、必要であるということだと思います。私は完了するのに十分なテストを書きますが、それ以上ではありません。
現在、単純なテストをスキップすることの問題の一部は、将来のリファクタリングによって、その単純なプロパティが多くのロジックで非常に複雑になる可能性があることです。テストを使用してモジュールの要件を検証できることが最善のアイデアだと思います。X を渡したときに Y を返す必要がある場合は、それをテストする必要があります。その後、後でコードを変更するときに、X が Y を与えることを確認し、A が B を与えるというテストを追加すると、その要件が後で追加されます。
最初の開発でテストを書くのに費やした時間は、1 回目または 2 回目のバグ修正で報われることがわかりました。3 か月間見ていないコードを見つけて、修正がすべてのケースをカバーし、「おそらく」何も壊れていないことを合理的に確信できることは、非常に価値があります。また、単体テストは、スタック トレースなどをはるかに超えてバグのトリアージに役立つこともわかります。アプリの個々の部分がどのように動作し、失敗するかを確認すると、それらが全体として動作または失敗する理由について大きな洞察が得られます。
ほとんどの場合、ロジックがある場合はテストします。これには、特にプロパティに複数のものが設定されている場合に、コンストラクタとプロパティが含まれます。
あまりにも多くのテストに関しては、議論の余地があります。すべての堅牢性をテストする必要があると言う人もいれば、効率的なテストのために、壊れる可能性があるもの (つまりロジック) だけをテストする必要があると言う人もいます。
個人的な経験から言えば、私はもっと第 2 陣営に傾倒しますが、もし誰かがすべてをテストすることに決めたとしても、それがやりすぎだとは言いません... 私にとっては少しやり過ぎかもしれませんが、彼らにとってはやり過ぎではありません。
ですから、いいえ - 一般的な意味でのテストが「多すぎる」ということはなく、個人に対してのみです。
テスト駆動開発とは、すべてのテストに合格したらコーディングをやめるということです。
プロパティのテストがない場合、なぜそれを実装する必要があるのでしょうか? 「不正な」割り当ての場合に予想される動作をテスト/定義しない場合、プロパティは何をすべきですか?
したがって、クラスが示すべきすべての動作をテストすることに完全に賛成です。「プリミティブ」プロパティを含みます。
このテストを簡単にするためTestFixtureに、値を設定/取得するための拡張ポイントを提供し、有効な値と無効な値のリストを取得し、プロパティが正しく機能するかどうかを確認する単一のテストを持つ単純な NUnit を作成しました。単一のプロパティをテストすると、次のようになります。
[TestFixture]
public class Test_MyObject_SomeProperty : PropertyTest<int>
{
private MyObject obj = null;
public override void SetUp() { obj = new MyObject(); }
public override void TearDown() { obj = null; }
public override int Get() { return obj.SomeProperty; }
public override Set(int value) { obj.SomeProperty = value; }
public override IEnumerable<int> SomeValidValues() { return new List() { 1,3,5,7 }; }
public override IEnumerable<int> SomeInvalidValues() { return new List() { 2,4,6 }; }
}
ラムダと属性を使用すると、これはさらにコンパクトに記述できます。MBUnit には、そのようなことに対するネイティブ サポートさえあると思います。ただし、ポイントは、上記のコードがプロパティの意図を捉えていることです。
PS: おそらく、PropertyTestには、オブジェクトの他のプロパティが変更されていないことを確認する方法も必要です。うーん.. 製図板に戻ります。
実現可能な最大のカバレッジに到達するために単体テストを行います。一部のコードに到達できない場合は、カバレッジが可能な限り完全になるまでリファクタリングします
盲目的な書き込みテストが終わったら、通常、各バグを再現する 1 つのテスト ケースを作成します。
私は、コード テストと統合テストを区別することに慣れています。統合テスト中 (これも単体テストですが、コンポーネントのグループに対するものであるため、単体テストの目的とは正確には異なります)、要件が正しく実装されているかどうかをテストします。
そのため、テストを作成してプログラミングを推進すればするほど、テストの粒度のレベルについて心配する必要がなくなります。振り返ってみると、動作を検証するという目標を達成するために可能な限り単純なことをしているようです。これは、コードが要求どおりに動作しているという信頼の層を生成していることを意味しますが、これは、コードにバグがないという絶対的な保証とは見なされません。標準的な動作をテストし、1 つまたは 2 つのエッジ ケースをテストしてから、設計の次の部分に進むことが正しいバランスだと思います。
これがすべてのバグをカバーするわけではなく、他の従来のテスト方法を使用してこれらをキャプチャすることを認めます。
それについて読めば読むほど、いくつかの単体テストはいくつかのパターンに似ていると思います: 不十分な言語の匂い。
単純なゲッターが実際に正しい値を返すかどうかをテストする必要がある場合は、ゲッター名とメンバー変数名が混在している可能性があるためです。ruby の 'attr_reader :name' を入力すると、これはもう起こりません。Javaでは不可能です。
ゲッターが自明でなくなった場合でも、テストを追加できます。
一般的に、私は、機能する必要があるとわかっている入力と出力から始めます。次に、バグを修正するときに、修正した内容が確実にテストされるように、さらにテストを追加します。それは有機的で、私にとってはうまく機能します。
あまりにも多くのテストを行うことができますか? おそらくですが、アプリケーションがどれほどミッション クリティカルであるかにもよりますが、一般的には注意を怠ったほうがよいでしょう。
気になるソースコードをテスト。
間違いを犯さない限り、非常に自信のあるコード部分をテストするのには役に立ちません。
バグ修正をテストして、バグを修正するのが最初で最後になるようにします。
あいまいなコード部分の信頼性を得るためにテストして、知識を作成します。
既存の機能を壊さないように、大規模および中程度のリファクタリングの前にテストしてください。
この回答は、その重要性/重要性のために単体テストを行うことがわかっている特定のメソッドに使用する単体テストの数を把握するためのものです。McCabe によるBasis Path Testing手法を使用すると、単純な「ステートメント カバレッジ」または「分岐カバレッジ」よりも定量的に優れたコード カバレッジの信頼性を得るために、次のことができます。
副作用のない単純なセッター/ゲッター メソッドの単体テストは行いません。しかし、私は他のすべてのパブリック メソッドの単体テストを行います。アルゴリズムですべての境界条件のテストを作成し、単体テストのカバレッジを確認しようとしています。
大変な作業ですが、それだけの価値があると思います。デバッガーでコードをステップ実行するよりも、コード (コードのテストであっても) を書きたいと思います。コード - ビルド - デプロイ - デバッグ サイクルは非常に時間がかかり、ビルドに統合したユニット テストが徹底的に行われるほど、そのコード - ビルド - デプロイ - デバッグ サイクルに費やす時間が短縮されます。
なぜアーキテクチャをコーディングしているのか、あなたは言いませんでした。しかし、Java の場合、 Maven 2、JUnit、DbUnit、Cobertura、およびEasyMockを使用します。
ビジネスロジックの「コア」ですべてをテストする必要があると思います。ゲッターとセッターも、受け入れたくない負の値または null 値を受け入れる可能性があるためです。時間があれば (常に上司に依存します)、他のビジネス ロジックと、これらのオブジェクトを呼び出すすべてのコントローラーをテストすることをお勧めします (単体テストから統合テストにゆっくりと進みます)。