TDDについて私が見つけたのは、テストをセットアップするのに時間がかかり、自然に怠け者であるということです。私は常にできるだけ少ないコードを書きたいと思っています。私が最初に行うことは、コンストラクタがすべてのプロパティを設定したことをテストすることですが、これはやり過ぎですか?
私の質問は、どのレベルの粒度でユニットテストを書くのですか?
..そして、テストしすぎるケースはありますか?
テストではなく動作するコードに対して報酬が支払われるため、私の哲学は、特定の信頼レベルに到達するためにできるだけテストしないことです(この信頼レベルは業界標準に比べて高いと思いますが、それはただのhub慢です) 。通常、ある種の間違い(コンストラクターで間違った変数を設定するなど)を行わない場合は、テストしません。私はテストエラーの意味を理解する傾向があるため、複雑な条件付きのロジックがある場合は特に注意が必要です。チームでコーディングするとき、私は戦略を変更して、集合的に間違っている傾向があるコードを慎重にテストします。
この哲学に基づいて、さまざまな人がさまざまなテスト戦略を立てますが、コーディングが内部ループにどのように最適であるかを理解する未熟な状態を考えると、それは理にかなっています。今から10、20年後には、どのテストを書くか、どのテストを書かないか、どのように違いを伝えるかという、より普遍的な理論が生まれるでしょう。それまでの間、実験は順調に行われているようです。
破損する可能性のあるもの、およびEdgeのケースについて単体テストを作成します。その後、バグの修正を作成する前に、バグレポートが届くとテストケースを追加する必要があります。開発者は、次のことを確信できます。
添付のコメントによると-ユニットテストを記述するこのアプローチは、特定のクラスで多くのバグが時間の経過とともに発見された場合、問題を引き起こす可能性があると思います。これはおそらく裁量が役立つ場合です-再発する可能性が高いバグの単体テストを追加するか、それらの再発が深刻な問題を引き起こす場合。これらのシナリオでは、単体テストでの統合テストの測定が役立つことがわかりました。コードパスの上位のコードをテストすると、コードパスの下位をカバーできます。
すべてをできるだけシンプルにする必要がありますが、シンプルにする必要はありません。 -A.アインシュタイン
TDDについて最も誤解されていることの1つは、TDDの最初の言葉です。テスト。それが、BDDが登場した理由です。なぜなら、最初のDが重要なDである、つまりDrivenであることを人々が本当に理解していなかったからです。私たちは皆、テストについて少しばかり考え、そして設計の推進について少しから少し考えがちです。そして、これはあなたの質問に対する漠然とした答えだと思いますが、おそらく実際にテストしているものではなく、コードをどうやって動かすかを考えるべきです。それは、カバレッジツールがあなたを助けることができるものです。デザインは非常に大きく、より問題の多い問題です。
「すべて」のテストを提案する人へint square(int x)
のようなメソッドを「完全にテスト」するには、共通言語および一般的な環境で約40億のテストケースが必要であることを認識してください。
実際、それよりさらに悪いのは、メソッドvoid setX(int newX)
にもx
以外のメンバーの値を変更する義務があるnot _obj.y
_、_obj.z
_などがobj.setX(42);
を呼び出した後も変更されないことをテストしていますか?
「すべて」のサブセットをテストすることのみが実用的ですこれを受け入れたら、信じられないほど基本的な動作をテストしないことを検討することがより好まれます。すべてのプログラマーには、バグの場所の確率分布があります。賢明なアプローチは、バグの可能性が高いと推定されるテスト地域にエネルギーを集中させることです。
古典的な答えは、「壊れる可能性のあるものをすべてテストする」ことです。これは、setまたはget以外の処理を行わないsetterおよびgetterのテストは多すぎるテストであり、時間をかける必要がないことを意味すると解釈します。あなたのIDEがあなたのためにそれらを書いていない限り、あなたもそうするかもしれません。
コンストラクターnotでプロパティを設定すると、後でエラーが発生する可能性がある場合、プロパティが設定されていることをテストするのはやり過ぎではありません。
私が書くクラスの仮定をカバーするテストを書きます。テストは要件を実施します。基本的に、たとえばxが3にならない場合は、その要件をカバーするテストがあることを確認します。
常に、条件をカバーするテストを作成しないと、「人間」のテスト中に後で発生します。確かにそれを書くつもりですが、私はそれらを早くキャッチしたいです。ポイントは、テストは(おそらく)退屈ですが、必要だということだと思います。完成するのに十分なテストを書いていますが、それ以上のものはありません。
現在、単純なテストをスキップすることの問題の一部は、将来、リファクタリングにより、その単純なプロパティが多くのロジックで非常に複雑になる可能性があることです。最善のアイデアは、テストを使用してモジュールの要件を検証できることです。 Xを渡すときにYを戻す必要がある場合は、それがテスト対象です。その後、後でコードを変更するときに、XがYを提供することを確認でき、その要件が後から追加されるときに、Aのテストを追加してBを提供できます。
最初の開発テストに費やす時間は、最初または2番目のバグ修正で報われることがわかりました。 3か月間見ていないコードをピックアップし、修正がすべてのケースをカバーし、「おそらく」何も壊さないことを合理的に確認する機能は非常に貴重です。また、ユニットテストは、スタックトレースなどをはるかに超えてバグのトリアージに役立つことがわかります。アプリの個々の部分がどのように機能し、失敗するかを見ると、バグが全体として機能するか失敗するかについての大きな洞察が得られます。
ほとんどの場合、そこにロジックがある場合はテストします。これには、特にプロパティに複数のものが設定されている場合、コンストラクターとプロパティが含まれます。
あまりにも多くのテストに関しては、議論の余地があります。すべての堅牢性をテストする必要があると言う人もいれば、効率的なテストのために、破損する可能性があるもの(つまり、ロジック)だけをテストする必要があると言う人もいます。
個人的な経験からだけで、2番目のキャンプに傾倒したいと思いますが、誰かがすべてをテストすることに決めたとしても、それが多すぎるとは言いません...私にとっては少しやり過ぎかもしれませんが、彼らにとっては多すぎません。
だから、いいえ-一般的な意味での「多すぎる」テストというのは、個人だけのためではない、と言うでしょう。
テスト駆動開発とは、すべてのテストに合格するとコーディングを停止することを意味します。
プロパティのテストがない場合、なぜそれを実装する必要がありますか? 「不正な」割り当ての場合に予想される動作をテスト/定義しない場合、プロパティはどうすればよいですか?
したがって、クラスが示すすべての動作を完全にテストします。 「プリミティブ」プロパティを含む。
このテストを簡単にするために、値を設定/取得するための拡張ポイントを提供し、有効な値と無効な値のリストを取得し、プロパティが正しく機能するかどうかを確認する単一のテストを行う単純なNUnit TestFixture
を作成しました。単一のプロパティのテストは次のようになります。
[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は、そのようなことをネイティブでサポートしています。ただし、ポイントは、上記のコードがプロパティの意図をキャプチャすることです。
追伸:おそらくPropertyTestには、オブジェクトのotherプロパティが変更されなかったことを確認する方法も必要です。うーん..図面に戻ります。
最大の実行可能範囲に到達するために、ユニットテストを行います。コードに到達できない場合は、カバレッジができるだけいっぱいになるまでリファクタリングします
筆記試験の盲検化が終わった後、私は通常、各バグを再現するテストケースを1つ作成します
コードテストと統合テストを区別するのに慣れています。統合テスト中に(ユニットテストでもコンポーネントのグループで行われるため、ユニットテストの正確な対象ではありません)、要件が正しく実装されるかどうかをテストします。
したがって、テストを作成してプログラミングを推進すればするほど、テストの粒度のレベルについて心配することは少なくなります。振り返ってみると、behaviourを検証するという私の目標を達成するために、できる限り単純なことをしているようです。これは、私のコードが私が要求することをしているという自信の層を生成していることを意味しますが、これは私のコードにバグがないという絶対的な保証とは見なされません。正しいバランスは、標準的な動作をテストすることであると感じ、場合によってはエッジケースを1つまたは2つ使用してから、設計の次の部分に進みます。
これですべてのバグがカバーされるわけではないことを承諾し、他の従来のテスト方法を使用してこれらをキャプチャします。
この答えは、特定のメソッドに使用する単体テストの数を決定するためのものであり、その重要性/重要性のために単体テストを行う必要があることがわかっています。 McCabeのBasis Path Testingテクニックを使用すると、単純な「ステートメントカバレッジ」または「ブランチカバレッジ」よりもコードカバレッジの信頼性を定量的に高めるために、次のことができます。
気になるソースコードをテストしてください。
間違いを犯さない限り、非常に自信があるコードの部分をテストするのには役立ちません。
バグ修正をテストして、バグを修正するのが最初と最後になるようにします。
あいまいなコード部分の信頼性をテストして、知識を作成します。
既存の機能を壊さないように、重度および中度のリファクタリングの前にテストします。
一般的に、私は小さなものから始めて、入力と出力が機能する必要があることを知っています。次に、バグを修正する際に、修正したものがテストされることを確認するためのテストを追加します。それはオーガニックで、私にとってはうまくいきます。
テストしすぎませんか?おそらく、しかし、一般的に注意を怠る方が良いでしょう。ただし、アプリケーションがどれほどミッションクリティカルであるかに依存します。
私がそれについてもっと読むほど、いくつかのユニットテストはいくつかのパターンのようだと思う:不十分な言語の匂い。
取るに足らないゲッターが実際に正しい値を返すかどうかをテストする必要がある場合、それはゲッター名とメンバー変数名を混在させる可能性があるためです。 Rubyの「attr_reader:name」を入力すると、これはもう起こりません。 Javaでは不可能です。
Ifゲッターが自明でない場合、テストを追加できます。
ビジネスロジックの「コア」ですべてをテストする必要があると思います。ゲッターとセッターも負の値または受け入れたくないヌル値を受け入れる可能性があるためです。時間がある場合は(常に上司に依存します)、他のビジネスロジックとこれらのオブジェクトを呼び出すすべてのコントローラーをテストすることをお勧めします(ユニットテストから統合テストにゆっくりと進みます)。
私は副作用のない単純なセッター/ゲッターメソッドを単体テストしません。しかし、私は他のすべてのパブリックメソッドを単体テストします。私は、アルゴリズムのすべての境界条件のテストを作成し、ユニットテストのカバレッジを確認しようとしています。
その多くの仕事が、私はそれの価値があると思います。デバッガーでコードをステップ実行するよりも、コードをテストします(コードをテストする場合でも)。 code-build-deploy-debugサイクルは非常に時間がかかり、ビルドに統合した単体テストがより徹底的になればなるほど、そのcode-build-deploy-debugサイクルを実行する時間が短くなります。
アーキテクチャもコーディングしている理由を言わなかった。しかし、Java私は Maven 2 、 JUnit 、 DbUnit 、 Cobertura を使用します、& EasyMock 。