web-dev-qa-db-ja.com

TDDと単体テストに関する質問

私はジュニアソフトウェア開発者であり、自分自身をより良くするために業界の慣行のいくつかを研究しています。私は単体テストを簡単に見てきましたが、大量の単体テストを作成する余分な時間によってコードがどのように改善されるかわかりません。

物事を見通しに入れるには:

  1. 私が取り組んでいるプロジェクトは小規模です
  2. 私はそのプロジェクトの唯一の開発者です(通常)
  3. プロジェクトはすべてオーダーメイドのアプリケーションです

私が最も得ていないのは、単体テストで価格計算関数(曜日や祝日などに依存する可能性があるため、その関数に20〜40行を想定)があるかどうかをどのように判断できるかです。正しい?すべてのコードを記述し、デバッグセッションに参加して、最終的にすべてのコードをテストする方が早いのではないでしょうか。

フォームベースの例をいただければ幸いです(MSDNビデオから見た例はすべてMVCであり、時間の無駄ですIMHO)。

6
Stuart Blackler

私が最も得られないのは、ユニットテストで価格計算関数(曜日や祝日などに依存する可能性があるため、その関数を20〜40行と想定できる)かどうかをどのように判断できるかです。正しい?すべてのコードを記述し、最終的にすべてのコードをテストするためにデバッグセッションを開始する方が速くはありませんか?

これを例にしましょう。座ってその(それを呼びましょう)30行のメソッドを作成する場合は、すべての可能性を考え、メソッドに書き込み、デバッグして、すべての可能性を考慮に入れようとします。曜日をチェックして銀行休業日に行き、バグを見つけた場合は、メソッドを変更する必要があります。これで、正しく機能するように簡単に変更できます。銀行休業日ではありますが、週末ではありません-すでに週末をチェックして機能しているため、再テストを忘れる可能性があります。これは、バグがアプローチによって製品に忍び寄るシナリオの1つにすぎません。

もう1つの問題は、過度の注意を払わなければ、実際には発生しない条件のコードを簡単に追加できることです。不要なコードはプロジェクトに複雑さを加え、複雑さはデバッグやその他のメンテナンスタスクを困難にします。

TDDはこれらの問題からどのようにあなたを保護しますか?まず、最も単純なケースと、それを渡す最も単純なコードを記述します。デフォルトの価格が7ドルだとします。便利なので、このJava風に書きますが、このアプローチはどの言語でも機能します。

_public void testDefaultPrice() throws Exception {
    assertEquals(7, subject.getPrice());
}

public int getPrice() {
    return 7;
}
_

簡単ですよね?不完全ですが、私たちが行った限りでは正しいです。

ここで、週末の価格は9ドルである必要があると言います。しかし、そこに着く前に、週末を構成する日を知る必要があります。

_public void testWeekend() throws Exception {
    assertTrue(Schedule.isWeekend(Weekday.Sunday));
}

public boolean isWeekend(Weekday.Day day) {
    return true;
}
_

これまでのところとても良い-そしてまだ非常に不完全です。

_public void testWeekend() throws Exception {
    assertTrue(Schedule.isWeekend(Weekday.Sunday));
    assertTrue(Schedule.isWeekend(Weekday.Saturday));
    assertFalse(Schedule.isWeekend(Weekday.Monday));
}

public boolean isWeekend(Weekday.Day day) {
    return day == Weekday.Sunday || day == Weekday.Saturday;
}
_

メソッドを正確に実行したことを確信するために必要な数のアサーションを追加します。

ここで、元のクラスに戻ります。

_public void testDefaultPrice() throws Exception {
    assertEquals(7, subject.getPrice());
}

public void testWeekendPrice() throws Exception {
    subject.setWeekday(Weekday.Sunday);
    assertEquals(9, subject.getPrice());
}

public int getPrice() {
    if (Schedule.isWeekend(day))
        return 9;
    return 7;
}
_

そして、それは行きます。また、ここでコードのdesignをテストドライブしている方法と、そのおかげでどれだけ優れているかに注意してください。あなたのアプローチでは、ほとんどのプログラマーは週末のテストコードをgetPrice()の本体に組み込んでいたでしょうが、それは間違った場所です。多くのコードは、1日が週末かどうかを知りたい場合があります。このようにして、十分にテストされた単一の場所にそれを置くことができます。これにより、再利用が促進され、保守性が向上します。

23
Carl Manaster

「デバッグセッションに参加して、最終的にすべてのコードをテストしますか?」 かもしれないもっと速く...

初めて。

戻って何かを変更する必要がある場合は、別のセッションを行う必要があります。それから2週間後、彼らは他の何かを変更したいと考えており、別のセッションを行う必要があります。

最初の変更後、場合によっては最初の開発の直後でも、累積テスト時間は、初期の単体テストを記述し、その特定の変更のすべての変更でそれらを拡張するのにかかった時間をはるかに超えています。ユニットテストは、複雑な(複雑ではない!)コードを変更する自信を与え、現在の機能をどれも壊していないことを知ることができます。

11
Marjan Venema
  1. デバッガーを使用して各条件をステップスルーし、入力と出力を追跡する方が、おそらく速くはありません。
  2. 得られるのは、コードに変更が加えられた後も引き続き実行できるテストです。たとえば、将来、価格計算関数に新しい条件を追加すると、ソフトウェアが依存する他の条件の1つが反転します。失敗した単体テストは、節約の恩恵になります。デバッガーにアクセスして、各条件を再度テストすることを考える必要はありません。

私見では、特にあなただけがあなたのプロジェクトに取り組んでいるので、あなたの立場でのユニットテストの価値を評価することは本当に難しいです。

テストの利点とテクニックの詳細については、本を試してみます。

http://www.Amazon.com/Art-Unit-Testing-Examples-Net/dp/1933988274/ref=sr_1_1?ie=UTF8&qid=1310919437&sr=8-1

3
rkaregaran

あなたが尋ねたのは良いことです:)では、なぜTDDなのですか?次の質問に答えて、TDDの理由を理解できるかどうか試してみてください。

  1. プロジェクトは小さいです。つまり、作成した機能の背後にあるロジックのすべてと、そのようにコーディングした理由がわかっているということですか。
  2. 答えが「はい」の場合は、あなたが述べた他の事実を結合して、さらに一歩進んでみてください-あなたはプロジェクトの唯一の開発者です-通常。コードロジックを見るたびに、なぜコードロジックが配置されているのかを常に覚えていると思いますか?
  3. あなたの答えは「はい」です。もう少し詳しく見てみましょう。最後の質問の答えを思い出すのにどれくらい時間がかかりますか?

この時までに、私はあなたが写真を手に入れることを望みます。そうでない場合は、次のようになります。テストコードは、特定のロジックが配置されている理由を見つけるために、すぐに参照できる補足として機能します。これはあなたの生産性を助け、時間のかかる骨の折れるデバッグセッションよりも楽しい経験になります。

これらすべてとは別に、TDDを実践する理由は次のとおりです。

  1. テストはあなたが書いたコードを補完します。これは、特定のコードの目的を理解するために、後の時点でのあなただけでなく、プロジェクトに取り組むことになった開発者にとっても役立ちます。
  2. テストを書くことにより、防御的にプログラミングします。コードをリファクタリングしたり、新しい機能を追加したり、いくつかのバグを修正したりする場合があります。 (適切に記述されている場合)本番環境で条件によってトリガーされる不運なバグからユーザーを保護するのはテストスイートです(非本番環境では何らかの理由でテストされていないはずです)。本番環境で問題が発生した場合に、すべてのコーナーからバッシングを取得するのは厄介なことです。どうすればそれを可能な限りゼロ発生にすることができますか。防御する。適切なテストセットを作成します。
  3. 長期的には、防御的コーディングによりコードの品質が向上し、生産性が向上します。
  4. テストは、いくつかのチェックリストに基づいて手動テストを定期的に行うためのより高速な方法です。手動テストは探索的である可能性があります-異常なものをテストします。ユニットテストは、スモークテストの回帰スイートとして機能します。詳細については、伝説的な人物であるマーティンファウラーのユニットテストに関するBlikiを参照してください。
  5. 純粋なTDDを実行する場合(最初にテストを記述し、次にコードを記述します)、何を記述しようとしているのかをよりよく理解し、必要なコードのみを記述できるようにします。また、テストによって推進される進化的な設計の改善も体験できます。
0
karthiks

...私の計算価格関数(曜日や銀行休業日などに依存する可能性があるため、その関数には20〜40行を想定します)

コードの不安定な性質(価格設定ルールは頻繁に変更される可能性があります-テストも同様です)を考えると、テストを安価にしたいのですが、テストを完全にスキップしないでください。

複数の条件文(if-then-else)はいつでもtrueになる可能性があります。テストに焦点を当ててくださいルールの組み合わせの予期しない結果

目的は、プログラムがばかげた答えに到達するのを防ぐことです。各価格設定ルールは単独で判断すると正しい場合がありますが、それらの組み合わせにより、禁止されていた場所で割引を2回適用したり、割引を過充電にしたりするなど、予期しない結果が生じることがあります。

私がそれにアプローチする方法は次のとおりです。

  • カバーしたい「テストケース」の表を作成します。テストケースのセットは、価格計算ルールのすべて、およびそれらの組み合わせを実行する必要があります。繰り返しまたはばかげていると思われるケースを削除します。
  • 残りのケースについては、予想される価格を手作業で計算してみてください。あなたは最善を尽くさなければなりません常識この時点で:あなたのマネージャーによってあなたに与えられた価格設定ルールがばかげた答えに到達した場合、ルールは間違っているか、ルール。
  • 完了したら、コードを介してテストケースを実行します。プログラムの出力を印刷して、間違いがないか確認します。
  • 間違いがない場合は、期待される出力値をアサートして、テストプログラムを単体テストに変換します。これは、将来のコード変更によるバグを防ぐのに役立ちます。
0
rwong