97

あなたのほとんどは自動化されたテストをたくさん書いていて、単体テストの際にいくつかの一般的な落とし穴に遭遇したこともあると思います。

私の質問は、将来の問題を回避するために、テストを作成するための行動規則に従っていますか? より具体的に言えば、優れた単体テストの特性とは何か、またはテストをどのように作成するのか?

言語にとらわれない提案が奨励されます。

4

18 に答える 18

93

ソースをプラグインすることから始めましょう - JUnit を使用した Java での実用的な単体テスト (C#-Nunit を使用したバージョンもあります..しかし、私はこれを持っています..ほとんどの場合、その不可知論的です.推奨.)

良いテストはA TRIPである必要があります(頭字語は十分に粘着性がありません。これが正しいことを確認するために引き出さなければならなかった本のチートシートのプリントアウトがあります..)

  • 自動: テストの呼び出しと PASS/FAIL の結果のチェックは自動化する必要があります
  • 徹底的:カバレッジ。バグはコード内の特定の領域に集中する傾向がありますが、すべての主要なパスとシナリオを必ずテストしてください。テストされていない領域を知る必要がある場合は、ツールを使用してください。
  • Repeatable : テストは毎回同じ結果を生成する必要があります..毎回。テストは、制御不能なパラメーターに依存するべきではありません。
  • 独立者:非常に重要です。
    • テストでは、一度に1 つのことだけをテストする必要があります。すべてのアサーションが 1 つの機能/動作をテストしている限り、複数のアサーションは問題ありません。テストが失敗した場合、問題の場所を特定する必要があります。
    • テストは互いに依存すべきではありません- 分離されています。テスト実行の順序に関する仮定はありません。セットアップ/ティアダウンを適切に使用して、各テストの前に「白紙の状態」であることを確認します
  • プロフェッショナル: 長期的には、本番環境と同じくらい多くのテスト コードを使用することになります (それ以上ではないにしても)。したがって、テスト コードの優れた設計と同じ基準に従います。意図を明らかにする名前を持つ適切に分解されたメソッドクラス、重複なし、適切な名前のテストなど。

  • 優れたテストも高速で実行されます。実行に 0.5 秒以上かかるテストはすべて、作業する必要があります。テスト スイートの実行に時間がかかるほど、実行頻度が低くなります。開発者が実行の間に忍び込もうとする変更が多いほど..何かが壊れた場合..どの変更が原因であるかを突き止めるのに時間がかかります。

更新 2010-08:

  • 読みやすい: これはプロフェッショナルの一部と見なすことができますが、強調してもしすぎることはありません。アシッド テストとは、チームのメンバーではない人を見つけて、テスト対象の動作を数分以内に把握するよう依頼することです。テストは、本番コードと同じように維持する必要があります。そのため、手間がかかったとしても読みやすいものにしてください。テストは対称的 (パターンに従う) かつ簡潔 (一度に 1 つの動作をテストする) である必要があります。一貫した命名規則を使用してください (例: TestDox スタイル)。「付随的な詳細」でテストを混乱させないでください.. ミニマリストになりましょう。

これらとは別に、他のほとんどは利益の低い作業を削減するガイドラインです。たとえば、「所有していないコードをテストしないでください」(サードパーティの DLL など)。ゲッターとセッターをテストしないでください。費用対効果の比率または欠陥の確率に注意してください。

于 2008-09-15T04:50:59.003 に答える
42
  1. 巨大なテストを書かないでください。「単体テスト」の「単体」が示すように、それぞれを可能な限りアトミック分離したものにします。必要に応じて、典型的なユーザー環境を手動で再作成するのではなく、モック オブジェクトを使用して前提条件を作成します。
  2. 明らかに機能するものをテストしないでください。サード パーティ ベンダーのクラスをテストしないでください。特に、コーディングするフレームワークのコア API を提供しているベンダーはそうです。たとえば、ベンダーの Hashtable クラスに項目を追加するテストをしないでください。
  3. NCover などのコード カバレッジ ツールを使用して、まだテストしていないエッジ ケースを発見することを検討してください。
  4. 実装にテストを書いてみてください。テストは、実装が準拠する仕様のようなものと考えてください。参照。また、テスト駆動開発のより具体的なブランチである動作駆動開発。
  5. 一貫性を保ちます。コードの一部に対してのみテストを作成する場合、ほとんど役に立ちません。チームで作業していて、他のメンバーの一部または全員がテストを作成していない場合、それもあまり役に立ちません。テストの重要性 (および時間節約の特性) を自分自身と他のすべての人に納得させるか、気にしないでください。
于 2008-09-14T15:36:26.730 に答える
41

ここでの回答のほとんどは、実際にテスト自体を作成する (方法) というよりも、一般的な単体テストのベスト プラクティス (いつ、どこで、なぜ、何を行うか) に対処しているようです。質問は「どのように」の部分でかなり具体的に思えたので、私が会社で行った「ブラウンバッグ」のプレゼンテーションから引用して、これを投稿しようと思いました.

Womp のライティング テストの 5 つの法則:


1. 長くてわかりやすいテスト メソッド名を使用します。

   - Map_DefaultConstructorShouldCreateEmptyGisMap()
   - ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
   - Dog_Object_Should_Eat_Homework_Object_When_Hungry()

2. テストをArrange/Act/Assert スタイルで記述します。

  • この組織戦略はしばらくの間存在し、多くのことを呼んでいましたが、最近「AAA」の頭字語が導入されたことは、これを理解するための優れた方法でした. すべてのテストを AAA スタイルと一致させることで、読みやすく、維持しやすくなります。

3. アサートには常に失敗メッセージを提供します。

Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element 
processing events was raised by the XElementSerializer");
  • ランナーアプリケーションで何が失敗したかを明らかにする、シンプルでありながらやりがいのあるプラクティス。メッセージを提供しない場合、通常、失敗の出力に「期待される真、偽であった」のようなものが表示されるため、実際にテストを読んで何が問題なのかを調べる必要があります。

4. テストの理由をコメントしてください。ビジネス上の仮定は何ですか?

  /// A layer cannot be constructed with a null gisLayer, as every function 
  /// in the Layer class assumes that a valid gisLayer is present.
  [Test]
  public void ShouldNotAllowConstructionWithANullGisLayer()
  {
  }
  • これは当たり前のことのように思えるかもしれませんが、この方法により、そもそもテストの背後にある理由を理解していない人からテストの整合性を守ることができます。多くのテストが完全に問題なく削除または変更されるのを見てきましたが、それは単純に、テストが検証している仮定をその人が理解していなかったからです。
  • テストが些細なものであるか、メソッド名が十分に説明的である場合は、コメントをオフのままにしてもかまいません。

5. すべてのテストは、触れたリソースの状態を常に元に戻す必要があります

  • 実際のリソースを扱うことを避けるために、可能な限りモックを使用してください。
  • クリーンアップはテスト レベルで行う必要があります。テストは、実行順序に依存してはなりません。
于 2009-05-06T20:18:37.007 に答える
17

これらの目標を念頭に置いてください (Meszaros の本 xUnit Test Patterns から改作)

  • テストは、リスクを導入するのではなく、リスクを軽減する必要があります。
  • テストは簡単に実行できる必要があります。
  • システムがテストを中心に進化するにつれて、テストは簡単に維持できる必要があります

これを簡単にするためのいくつかのこと:

  • テストが失敗する理由は 1 つだけです。
  • テストは 1 つのことだけをテストする必要があります
  • テストの依存関係を最小限に抑える (データベース、ファイル、UI などへの依存関係がない)

xUnit フレームワークでも統合テストを実行できることを忘れないでください。ただし、統合テストと単体テストは分けておいてください。

于 2008-09-14T15:31:22.937 に答える
9

テストは分離する必要があります。1 つのテストが別のテストに依存するべきではありません。さらに、テストは外部システムに依存すべきではありません。つまり、コードが依存するコードではなく、コードをテストしてください。これらの相互作用は、統合テストまたは機能テストの一部としてテストできます。

于 2008-09-14T16:51:14.133 に答える
9

優れた単体テストのいくつかの特性:

  • テストが失敗した場合、問題がどこにあるのかがすぐに明らかになるはずです。問題を追跡するためにデバッガーを使用する必要がある場合、テストの粒度が十分ではありません。ここでは、テストごとにアサーションを 1 つだけ持つことが役立ちます。

  • リファクタリングすると、テストが失敗することはありません。

  • テストは非常に高速に実行する必要があるため、実行することをためらうことはありません。

  • すべてのテストは常にパスする必要があります。非決定的な結果はありません。

  • 単体テストは、実稼働コードと同様に、十分に考慮されている必要があります。

@Alotor: ライブラリの外部 API での単体テストのみを行うべきだと提案している場合は、同意しません。外部呼び出し元に公開しないクラスを含め、各クラスの単体テストが必要です。(ただし、プライベート メソッドのテストを作成する必要がある場合は、リファクタリングする必要があります。 )


編集:「テストごとに1つのアサーション」による重複についてのコメントがありました。具体的には、シナリオをセットアップするためのコードがあり、それについて複数のアサーションを作成したいが、テストごとに 1 つのアサーションしかない場合、複数のテストでセットアップを複製する可能性があります。

私はその方法をとりません。代わりに、シナリオごとにテスト フィクスチャを使用します。大まかな例を次に示します。

[TestFixture]
public class StackTests
{
    [TestFixture]
    public class EmptyTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
        }

        [TestMethod]
        [ExpectedException (typeof(Exception))]
        public void PopFails()
        {
            _stack.Pop();
        }

        [TestMethod]
        public void IsEmpty()
        {
            Assert(_stack.IsEmpty());
        }
    }

    [TestFixture]
    public class PushedOneTests
    {
        Stack<int> _stack;

        [TestSetup]
        public void TestSetup()
        {
            _stack = new Stack<int>();
            _stack.Push(7);
        }

        // Tests for one item on the stack...
    }
}
于 2008-09-14T16:22:22.703 に答える
7

あなたが求めているのは、テスト対象のクラスの動作の描写です。

  1. 期待される動作の検証。
  2. エラーケースの検証。
  3. クラス内のすべてのコード パスをカバーします。
  4. クラス内のすべてのメンバー関数を実行します。

基本的な意図は、クラスの振る舞いに対する自信を高めることです。

これは、コードのリファクタリングを検討する場合に特に役立ちます。Martin Fowlerの Web サイトに、テストに関する興味深い記事があります。

HTH。

乾杯、

ロブ

于 2008-09-14T15:37:52.857 に答える
7

テストは本来失敗するはずです。次に、それらをパスするコードを作成する必要があります。そうしないと、バグがあり、常にパスするテストを作成するリスクがあります。

于 2008-09-14T15:38:02.327 に答える
6

前述のPragmaticUnitTestingの本のRightBICEPの頭字語が好きです。

  • 正しい:結果は正しいですか?
  • B:すべての境界条件は正しいですか?
  • I :逆の関係を確認できますか?
  • C:他の手段を使用して結果をクロスチェックできますか?
  • E:エラー状態を強制的に発生させることはできます
  • P:パフォーマンス特性は範囲内ですか

個人的には、正しい結果が得られることを確認し(1 + 1は加算関数で2を返す必要があります)、考えられるすべての境界条件を試してみることで、かなり遠くまで到達できると思います(たとえば、2つの数値の合計を使用するなど)。 add関数の整数の最大値よりも大きい)、ネットワーク障害などのエラー状態を強制します。

于 2008-09-15T11:23:57.903 に答える
6

優れたテストは保守可能である必要があります。

複雑な環境でこれを行う方法がよくわかりません。

コードベースが数百行または数百万行のコードに達し始めると、すべての教科書がばらばらになり始めます。

  • チームの相互作用が爆発する
  • テストケースの爆発数
  • コンポーネント間の相互作用が爆発します。
  • すべての単体テストをビルドする時間がビルド時間の重要な部分になります
  • API の変更は、何百ものテスト ケースに波及する可能性があります。生産コードの変更は簡単でしたが。
  • プロセスを正しい状態にシーケンスするために必要なイベントの数が増え、テストの実行時間が長くなります。

優れたアーキテクチャーは相互作用の爆発の一部を制御できますが、必然的に、システムがより複雑になるにつれて、自動テストシステムもそれに伴って成長します。

ここから、トレードオフに対処しなければならなくなります。

  • 外部 API のみをテストします。それ以外の場合、内部をリファクタリングすると、テスト ケースの大幅なやり直しが発生します。
  • カプセル化されたサブシステムがより多くの状態を保持するため、各テストのセットアップとティアダウンはより複雑になります。
  • 毎晩のコンパイルと自動テストの実行は数時間にもなります。
  • コンパイルと実行時間の増加は、設計者がすべてのテストを実行しない、または実行しないことを意味します
  • テストの実行時間を短縮するには、テストの順序付けを検討して、セットアップとティアダウンを減らす必要があります

次のことも決定する必要があります。

コード ベースのどこにテスト ケースを保存しますか?

  • テストケースをどのように文書化しますか?
  • テストケースのメンテナンスを節約するためにテストフィクスチャを再利用できますか?
  • 毎晩のテスト ケースの実行が失敗するとどうなりますか? トリアージは誰が行うのですか?
  • モック オブジェクトをどのように維持していますか? 独自のフレーバーのモック ロギング API をすべて使用する 20 個のモジュールがある場合、API の変更はすぐに波及します。テスト ケースが変更されるだけでなく、20 個のモック オブジェクトが変更されます。これらの 20 個のモジュールは、さまざまなチームによって数年にわたって作成されました。その古典的な再利用の問題。
  • 個人とそのチームは、自動化されたテストの価値を理解していますが、他のチームのやり方が気に入らないだけです。:-)

私は永遠に続けることができますが、私のポイントは次のとおりです。

テストは保守可能である必要があります。

于 2009-04-24T18:46:27.967 に答える
5

これらの原則については、この MSDN Magazine の記事で取り上げました。この記事は、すべての開発者が読むことが重要だと思います。

私が「良い」単体テストを定義する方法は、次の 3 つのプロパティを持っているかどうかです。

  • それらは読み取り可能です(名前付け、アサート、変数、長さ、複雑さ..)
  • それらは保守可能です(ロジックなし、指定されていない、状態ベース、リファクタリング..)
  • それらは信頼に値します(統合テストではなく、分離された正しいものをテストします..)
于 2008-09-28T19:35:18.313 に答える
4
  • ユニット テストは、ユニットの外部 API をテストするだけであり、内部動作をテストするべきではありません。
  • TestCase の各テストは、この API 内の 1 つの (そして 1 つだけの) メソッドをテストする必要があります。
    • 失敗ケースには、追加のテスト ケースを含める必要があります。
  • テストのカバレッジをテストする: ユニットがテストされると、このユニット内の行の 100% が実行されているはずです。
于 2008-09-14T15:40:17.250 に答える
2

Jay Fields は単体テストの作成について多くの優れたアドバイスを提供しており、最も重要なアドバイスをまとめた投稿があります。そこには、自分の文脈を批判的に考え、そのアドバイスが自分にとって価値があるかどうかを判断する必要があることが書かれています。ここには驚くべき答えがたくさんありますが、状況に応じてどれが最適かを決めるのはあなた次第です。それらを試してみて、臭いがする場合はリファクタリングしてください。

敬具

于 2008-09-15T03:52:27.350 に答える
1

テストが互いに依存すべきであることを除いて、私は「A TRIP」の回答を支持します!!!

なんで?

DRY - Don't Repeat Yourself - テストにも適用されます。テストの依存関係は、1) セットアップ時間の節約、2) フィクスチャ リソースの節約、3) 失敗の特定に役立ちます。もちろん、テスト フレームワークがファースト クラスの依存関係をサポートしている場合に限ります。そうでなければ、私は認めます、彼らは悪いです。

フォローアップhttp://www.iam.unibe.ch/~scg/Research/JExample/

于 2008-10-07T21:44:19.283 に答える
1

些細な 2 行の方法でうまくいくと思い込まないでください。ヌル テストの欠落、マイナス記号の配置ミス、微妙なスコープ エラーに悩まされるのを防ぐ唯一の方法は、簡単な単体テストを作成することです。

于 2008-09-14T15:52:44.787 に答える
0

多くの場合、単体テストはモック オブジェクトまたはモック データに基づいています。私は 3 種類の単体テストを書くのが好きです。

  • 「一時的な」単体テスト: 独自のモック オブジェクト/データを作成し、それを使用して機能をテストしますが、すべてを破棄し、痕跡を残しません (テスト データベースにデータがない場合と同様)。
  • 「永続的な」単体テスト: コード内の関数をテストし、後でより高度な機能が独自の単体テストで必要とするオブジェクト/データを作成します (これらの高度な機能が独自のモックオブジェクト/データのセットを毎回再作成することを回避します)。
  • 「永続ベース」単体テスト: 永続単体テストによって (別の単体テスト セッションで作成されたため) 既に存在するモック オブジェクト/データを使用した単体テスト。

ポイントは、すべての機能をテストできるようにするために、すべてを再生しないようにすることです。

  • すべてのモック オブジェクト/データが既に存在するため、私は非常に頻繁に 3 番目の種類を実行します。
  • モデルが変わるたびに、2番目の種類を実行します。
  • 最初のものを実行して、非常に基本的な機能をときどきチェックし、基本的なリグレッションをチェックします。
于 2008-09-14T21:04:14.133 に答える
0

機能テストとパフォーマンス テストの 2 種類のテストについて考え、それらを異なる方法で扱います。

それぞれに異なる入力とメトリックを使用します。テストの種類ごとに異なるソフトウェアを使用する必要がある場合があります。

于 2008-09-14T21:34:54.667 に答える
0

Roy Osherove の Unit Test Naming standardsで説明されている一貫したテスト命名規則を使用します。特定のテスト ケース クラスの各メソッドには、次の命名スタイル MethodUnderTest_Scenario_ExpectedResult があります。

    最初のテスト名セクションは、テスト中のシステムのメソッドの名前です。
    次は、テストされている特定のシナリオです。
    最後に、そのシナリオの結果です。

各セクションはアッパーキャメルケースを使用し、アンダースコアで区切られています。

テストを実行すると、テスト中のメソッドの名前でテストがグループ化されているため、これが役立つことがわかりました。また、他の開発者がテストの意図を理解できるようにするための規則を用意してください。

また、テスト対象のメソッドがオーバーロードされている場合は、メソッド名にパラメーターを追加します。

于 2010-08-14T17:41:52.867 に答える