web-dev-qa-db-ja.com

単体テストで何をテストする必要がありますか?

私は大学を卒業したばかりで、来週どこかで大学を始めます。単体テストを見てきましたが、あまり使用していません。みんながそのことについて話しているので、多分私はいくつかやるべきだと思いました。

問題は、whatがわからないということです。一般的なケースをテストする必要がありますか?エッジケース?関数が適切にカバーされていることをどのようにして知ることができますか?

私は常にテストは関数が特定のケースで機能することを証明する一方で、その関数が機能することを証明することはまったく役に立たないという恐ろしい感覚を常に持っています。

128
zneak

私の個人的な哲学はこれまでのところ:

  1. できるすべての一般的なケースをテストします。これは、変更を加えた後にそのコードが壊れたときにわかります(私の意見では、自動化された単体テストの最大の利点の1つです)。
  2. おそらくエラーがあると思われるいくつかの非常に複雑なコードのEdgeケースをテストします。
  3. バグを見つけたら、修正する前にカバーするテストケースを書いてください
  4. 誰かが殺す時間があるときはいつでも、重要度の低いコードにエッジケーステストを追加します。
124
Fishtoaster

これまでの大量の回答の中で、誰も 等価分割境界値分析 に触れていないため、目の前の質問に対する回答で重要な考慮事項があります。他のすべての回答は有用ではありますが、定性的ですが、ここでは定量的であることが可能であり、望ましいです。 @fishtoasterはいくつかの具体的なガイドラインを提供し、テストの定量化のカバーの下を覗き見しますが、等価分割と境界値分析により、より良いことができます。

equivalence partitioningでは、予想される結果に基づいて、すべての可能な入力のセットをグループに分割します。 1つのグループからの入力はすべて同等の結果をもたらすため、そのようなグループは同等クラスと呼ばれます。 (同等の結果はではないが同じ結果を意味することに注意してください。)

簡単な例として、小文字ASCII文字を大文字に変換する必要があるプログラムを考えてみます。他の文字はID変換を受ける必要があります。つまり、変更されないままです。等価クラスへの可能な分解の1つを次に示します。

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | 0-9!@#,/"... | 0-9!@#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

最後の列は、すべてを列挙した場合のテストケースの数を報告します。技術的には、@ fishtoasterのルール1により、52のテストケースを含めることができます。上記の最初の2行のテストケースはすべて「一般的なケース」に該当します。 @fishtoasterのルール2では、上記の行3と4の一部またはすべても追加されます。しかし、等価分割テストでは、各等価クラスのoneテストケースで十分です。 「a」、「g」、または「w」を選択すると、同じコードパスをテストします。したがって、52以上ではなく、合計4つのテストケースがあります。

境界値分析は、わずかな改良を推奨します。基本的に、等価クラスのすべてのメンバーが同等であるとは限らないことを示唆しています。つまり、境界の値も、それ自体でテストケースに値すると見なされます。 (これの簡単な正当化の1つは、悪名高い 1つずつのエラー !です。)したがって、各等価クラスに対して、3つのテスト入力を持つことができます。上記の入力ドメインを見ると、ASCII値の知識がある場合)、次のテストケース入力が考えられます。

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

(3つ以上の境界値を取得するとすぐに、元の等価クラスの描写を再考することをお勧めしますが、これは、私がそれらを修正するために戻らなかったほど簡単でした。)したがって、境界値分析は、完全なカバレッジの高い信頼性を持つ17のテストケースは、徹底的なテストを行う128のテストケースと比較して(言うまでもなく、組合せ論は、徹底的なテストは実際のアプリケーションでは実行不可能であることを示しています!)

68
Michael Sorens

おそらく私の意見はあまり人気がありません。しかし、私はあなたがユニットテストで経済的であることをお勧めします。ユニットテストが多すぎると、実際のコーディングではなくテストの維持に半分以上の時間を費やしてしまいます。

私はあなたがあなたの腸に悪い感情を持っているもの、または非常に重要かつ/または基本的なもののためのテストを書くことをお勧めします。 IMHO単体テストは、優れたエンジニアリングと防御コーディングの代わりにはなりません。現在、私は多かれ少なかれ使用できないプロジェクトに取り組んでいます。それは本当に安定していますが、リファクタリングするのは面倒です。実際、1年もこのコードに触れたことはなく、そのベースとなっているソフトウェアスタックは4年前のものです。どうして?ユニットテストが散らかっているため、正確には、ユニットテストと自動化された統合テストです。 (キュウリなどについて聞いたことがあるでしょうか?)そしてここが一番の特徴です:この(まだ)使用できないソフトウェアは、テスト主導の開発シーンのパイオニアである従業員が開発したものです。 :D

だから私の提案は:

  • テストの作成を開始します基本的なスケルトンを開発しました。そうしないと、リファクタリングが面倒になる場合があります。他の人のために開発する開発者は、最初から要件を正しく把握することはできません。

  • 単体テストをすばやく実行できることを確認してください。統合テスト(キュウリなど)がある場合、もう少し時間がかかっても問題ありません。しかし、長時間実行されるテストは楽しくありません。信じてください。 (人々はC++があまり人気がなくなった理由をすべて忘れています...)

  • このTDDはTDDエキスパートに任せてください。

  • そして、はい、予期しないことが予想される場所に応じて、エッジのケースに集中することもあれば、一般的なケースに集中することもあります。ただし、常に予期せぬ事態を予期する場合は、ワークフローと規律を再考する必要があります。 ;-)

20
Philip

最初にテスト駆動開発でテストする場合、失敗する単体テストを最初に作成しないと機能を追加できないため、カバレッジは90%以上になります。

事実の後にテストを追加する場合、 Michael Feathers によって レガシーコードで効果的に動作する のコピーを取得して、コードにテストを追加する方法と、コードをリファクタリングしてテストしやすくする方法の両方。

8
Paddyslacker

Test Driven Development のプラクティスに従っていくと、プロセスが並べ替えられguideなり、何をテストすべきかが自然にわかるようになります。開始するいくつかの場所:

テストが最初に来る

テストを記述する前にコードを記述しないでください。説明はRed-Green-Refactor-Repeatを参照してください。

回帰テストを作成します

バグに遭遇したら必ずテストケースを書き、それがfailsであることを確認してください。失敗したテストケースでバグを再現できない限り、実際には発見できません。

Red-Green-Refactor-Repeat

Red:まず、実装しようとしている動作の最も基本的なテストを記述します。作業中のクラスまたは関数を使用するいくつかのサンプルコードを書くときのこのステップについて考えてください。コンパイル/構文エラーがないこと、が失敗することを確認してください。これは明らかなはずです。コードを記述していないため、失敗するはずです。ここで学ぶ重要なことは、テストが少なくとも1回失敗したことが確認されない限り、テストが合格した場合に、何らかの偽の理由で実行したことが原因でテストが成功したとは決して言えないということです。

Green:実際にテストに合格する最も単純で愚かなコードを記述します。賢くしようとしないでください。明らかなEdgeケースがあることがわかっていても、テストで考慮に入れても、それを処理するコードを記述しないでください(ただし、Edgeケースについて忘れないでください:後で必要になります)。アイデアは、あなたが書くすべてのコード、すべてのif、すべてのtry: ... except: ...はテストケースによって正当化される必要があります。コードは、エレガント、高速、または最適化されている必要はありません。テストに合格したいだけです。

Refactor:コードをクリーンアップし、メソッド名を正しく取得します。テストがまだ成功しているかどうかを確認します。最適化。テストを再実行してください。

繰り返し:テストがカバーしなかったEdgeケースを覚えていますか?だから、今はその大きな瞬間です。その状況をカバーするテストケースを書き、失敗を観察し、コードを書き、成功することを確認し、リファクタリングします。

テストyourコード

あなたは特定のコードに取り組んでおり、これはまさにあなたがテストしたいものです。つまり、ライブラリ関数、標準ライブラリ、またはコンパイラをテストするべきではありません。また、「世界」をテストしないようにしてください。これには、外部Web APIの呼び出し、データベースを多用するものなどが含まれます。モックアップを試みることができるときはいつでも(同じインターフェイスに従いますが、静的な事前定義データを返すオブジェクトを作成します)。

6
Ryszard Szopa

単体テストの場合は、設計どおりに機能することをテストすることから始めます。それはあなたが書く最初のケースであるべきです。デザインの一部が「ジャンクを渡すと例外がスローされる」場合、それもデザインの一部であるため、テストしてください。

それから始めましょう。その最も基本的なテストを実行する経験を積むと、それで十分かどうかを学び始め、テストが必要なコードの他の側面を確認し始めます。

3
Bryan Oakley

標準的な答えは "壊れる可能性のあるすべてのものをテストする" です。

シンプルすぎて壊れないのは何ですか?データフィールド、頭の痛いプロパティアクセサー、および同様の定型オーバーヘッド。それ以外のものはおそらく、要件の特定可能な部分を実装しており、テストすることでメリットが得られる可能性があります。

もちろん、走行距離と作業環境の慣行は異なる場合があります。

0
Jeffrey Hantin