TDDについて私が見つけたのは、テストをセットアップするのに時間がかかることと、自然に怠惰であることです。私が最初に行うことは、コンストラクターがすべてのプロパティを設定したことをテストすることですが、これはやりすぎですか?
私の質問は、ユニットテストをどの程度の粒度で作成することですか。
..そして、テストしすぎるケースはありますか?
回答:
テストではなく機能するコードに対して支払いを受けるので、私の哲学は、特定のレベルの信頼性に到達するためにできるだけテストしないことです(このレベルの信頼性は業界標準と比較して高いと思いますが、それはただのやり過ぎかもしれません)。 。通常、ある種の間違い(コンストラクターでの間違った変数の設定など)を行わない場合は、テストしません。私はテストエラーを理解する傾向があるので、複雑な条件付きのロジックがある場合は特に注意が必要です。チームでコーディングするときは、全体として間違ってしまう傾向があるコードを注意深くテストするように戦略を変更します。
この哲学に基づくテスト戦略は人によって異なりますが、テストがコーディングの内部ループにどのように最適に適合するかについての理解が未熟であることを考えると、それは理にかなっています。今から10〜20年後には、どのテストを作成し、どのテストを作成しないか、どのように違いを区別するかについて、より普遍的な理論が確立されるでしょう。それまでの間、実験は順調に進んでいるようです。
壊れると予想されるもの、およびエッジケースの単体テストを記述します。その後、バグレポートが届いたら、バグの修正を書く前にテストケースを追加する必要があります。これにより、開発者は次のことを確信できます。
添付されたコメントによると-長期にわたって多くのバグが特定のクラスで発見された場合、ユニットテストを作成するこのアプローチは問題を引き起こす可能性があると思います。これはおそらく、裁量が役立つ場所です-再発する可能性のあるバグのみの単体テストを追加するか、それらの再発により深刻な問題が発生する場合です。ユニットテストでの統合テストの測定は、これらのシナリオで役立つことがわかりました。上位のコードパスのコードをテストすると、下位のコードパスをカバーできます。
すべてをできるだけ単純にする必要がありますが、単純にする必要はありません。-A.アインシュタイン
TDDについて最も誤解されていることの1つは、その最初の言葉です。テスト。そのため、BDDが登場しました。最初のDが重要なもの、つまりDrivenであることを人々は本当に理解していなかったからです。私たちは皆、テストについて少しずつ、そしてデザインの推進について少しずつ考える傾向があります。そして、これはあなたの質問に対する漠然とした答えだと思いますが、実際にテストしているものではなく、コードの駆動方法を検討する必要があります。これは、カバレッジツールが役立ちます。デザインはかなり大きく、問題の多い問題です。
「すべて」をテストすることを提案する人へ:のようなメソッドを「完全にテスト」するにint square(int x)
は、一般的な言語と一般的な環境で約40億のテストケースが必要であることを認識してください。
実際には、それよりもさらに悪いことだ:メソッドをvoid setX(int newX)
も義務付けられていないほか、他のメンバーの値を変更するx
-あなたはそれをテストしているobj.y
、obj.z
すべての呼び出し後に変更されないままなど、obj.setX(42);
?
「すべて」のサブセットをテストすることは実際的です。 これを受け入れると、信じられないほどの基本的な動作をテストしないことを検討するほうが口当たりが良くなります。すべてのプログラマーは、バグの場所の確率分布を持っています。スマートなアプローチは、バグの確率が高いと推定されるテスト領域にエネルギーを集中させることです。
古典的な答えは「壊れる可能性のあるものをすべてテストする」です。私はそれを、setまたはget以外に何もしないセッターおよびゲッターをテストすることはおそらくあまりにも多くのテストであり、時間をかける必要がないことを意味すると解釈します。あなたのIDEがあなたのためにそれらを書いていない限り、あなたもそうかもしれません。
プロパティを設定しないコンストラクターが後でエラーにつながる可能性がある場合、それらが設定されていることをテストしても過剰ではありません。
単純なテストをスキップする際の問題の一部は、将来的にはリファクタリングにより、その単純なプロパティが多くのロジックで非常に複雑になる可能性があります。モジュールの要件を検証するためにテストを使用できるというのが最良のアイデアだと思います。Xを渡したときにYを取り戻す必要がある場合は、それをテストします。次に、後でコードを変更するときに、XがYを提供することを確認でき、Aのテストを追加してBを提供できます(その要件が後で追加された場合)。
最初の開発中にテストを記述していた時間は、最初または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つのエッジケースをテストしてから、デザインの次の部分に進むと思います。
これですべてのバグがカバーされるわけではなく、他の従来のテスト方法を使用してこれらをキャプチャすることを受け入れます。
ビジネスロジックの「コア」のすべてをテストする必要があると思います。Getter ans Setterも、受け入れたくない負の値またはnull値を受け入れる可能性があるためです。時間がある場合(常に上司に依存します)、他のビジネスロジックとこれらのオブジェクトを呼び出すすべてのコントローラーをテストすることをお勧めします(単体テストから統合テストにゆっくりと進みます)。
副作用のない単純なセッター/ゲッターメソッドの単体テストは行いません。しかし、私は他のすべてのパブリックメソッドを単体テストします。私はアルゴリズムのすべての境界条件のテストを作成して、ユニットテストの範囲を確認しようとしています。
大変な作業ですが、それだけの価値があると思います。デバッガーでコードをステップ実行するよりも、コード(テストコードでさえ)を記述したい。code-build-deploy-debugサイクルは非常に時間がかかり、ビルドに統合した単体テストをより徹底的に行うと、そのcode-build-deploy-debugサイクルに費やす時間が短縮されます。
なぜあなたがコーディングしているアーキテクチャも、あなたは言わなかった。ただし、Javaの場合は、Maven 2、JUnit、DbUnit、Cobertura、およびEasyMockを使用します。
それについて読むほど、いくつかの単体テストはいくつかのパターンに似ていると思います。不十分な言語の匂い。
簡単なゲッターが実際に正しい値を返すかどうかをテストする必要があるのは、ゲッター名とメンバー変数名を混在させることができるためです。rubyの 'attr_reader:name'を入力すると、これは発生しなくなります。Javaでは不可能です。
場合は、あなたのゲッターは、これまでの非自明な取得あなたはまだそれのためにテストを追加することができます。
この回答は、特定のメソッドに使用する単体テストの数を、その重要度/重要性のために単体テストを実行したいことがわかっている場合に理解するためのものです。McCabeによるベーシスパステスト手法を使用すると、次のようにして、単純な「ステートメントカバレッジ」または「ブランチカバレッジ」よりもコードカバレッジの信頼性を定量的に向上させることができます。