あなたのほとんどは自動化されたテストをたくさん書いていて、単体テストの際にいくつかの一般的な落とし穴に遭遇したこともあると思います。
私の質問は、将来の問題を回避するために、テストを作成するための行動規則に従っていますか? より具体的に言えば、優れた単体テストの特性とは何か、またはテストをどのように作成するのか?
言語にとらわれない提案が奨励されます。
あなたのほとんどは自動化されたテストをたくさん書いていて、単体テストの際にいくつかの一般的な落とし穴に遭遇したこともあると思います。
私の質問は、将来の問題を回避するために、テストを作成するための行動規則に従っていますか? より具体的に言えば、優れた単体テストの特性とは何か、またはテストをどのように作成するのか?
言語にとらわれない提案が奨励されます。
ソースをプラグインすることから始めましょう - JUnit を使用した Java での実用的な単体テスト (C#-Nunit を使用したバージョンもあります..しかし、私はこれを持っています..ほとんどの場合、その不可知論的です.推奨.)
良いテストはA TRIPである必要があります(頭字語は十分に粘着性がありません。これが正しいことを確認するために引き出さなければならなかった本のチートシートのプリントアウトがあります..)
プロフェッショナル: 長期的には、本番環境と同じくらい多くのテスト コードを使用することになります (それ以上ではないにしても)。したがって、テスト コードの優れた設計と同じ基準に従います。意図を明らかにする名前を持つ適切に分解されたメソッドクラス、重複なし、適切な名前のテストなど。
優れたテストも高速で実行されます。実行に 0.5 秒以上かかるテストはすべて、作業する必要があります。テスト スイートの実行に時間がかかるほど、実行頻度が低くなります。開発者が実行の間に忍び込もうとする変更が多いほど..何かが壊れた場合..どの変更が原因であるかを突き止めるのに時間がかかります。
更新 2010-08:
これらとは別に、他のほとんどは利益の低い作業を削減するガイドラインです。たとえば、「所有していないコードをテストしないでください」(サードパーティの DLL など)。ゲッターとセッターをテストしないでください。費用対効果の比率または欠陥の確率に注意してください。
ここでの回答のほとんどは、実際にテスト自体を作成する (方法) というよりも、一般的な単体テストのベスト プラクティス (いつ、どこで、なぜ、何を行うか) に対処しているようです。質問は「どのように」の部分でかなり具体的に思えたので、私が会社で行った「ブラウンバッグ」のプレゼンテーションから引用して、これを投稿しようと思いました.
1. 長くてわかりやすいテスト メソッド名を使用します。
- Map_DefaultConstructorShouldCreateEmptyGisMap()
- ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
- Dog_Object_Should_Eat_Homework_Object_When_Hungry()
2. テストをArrange/Act/Assert スタイルで記述します。
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. すべてのテストは、触れたリソースの状態を常に元に戻す必要があります
これらの目標を念頭に置いてください (Meszaros の本 xUnit Test Patterns から改作)
これを簡単にするためのいくつかのこと:
xUnit フレームワークでも統合テストを実行できることを忘れないでください。ただし、統合テストと単体テストは分けておいてください。
テストは分離する必要があります。1 つのテストが別のテストに依存するべきではありません。さらに、テストは外部システムに依存すべきではありません。つまり、コードが依存するコードではなく、コードをテストしてください。これらの相互作用は、統合テストまたは機能テストの一部としてテストできます。
優れた単体テストのいくつかの特性:
テストが失敗した場合、問題がどこにあるのかがすぐに明らかになるはずです。問題を追跡するためにデバッガーを使用する必要がある場合、テストの粒度が十分ではありません。ここでは、テストごとにアサーションを 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...
}
}
あなたが求めているのは、テスト対象のクラスの動作の描写です。
基本的な意図は、クラスの振る舞いに対する自信を高めることです。
これは、コードのリファクタリングを検討する場合に特に役立ちます。Martin Fowlerの Web サイトに、テストに関する興味深い記事があります。
HTH。
乾杯、
ロブ
テストは本来失敗するはずです。次に、それらをパスするコードを作成する必要があります。そうしないと、バグがあり、常にパスするテストを作成するリスクがあります。
前述のPragmaticUnitTestingの本のRightBICEPの頭字語が好きです。
個人的には、正しい結果が得られることを確認し(1 + 1は加算関数で2を返す必要があります)、考えられるすべての境界条件を試してみることで、かなり遠くまで到達できると思います(たとえば、2つの数値の合計を使用するなど)。 add関数の整数の最大値よりも大きい)、ネットワーク障害などのエラー状態を強制します。
優れたテストは保守可能である必要があります。
複雑な環境でこれを行う方法がよくわかりません。
コードベースが数百行または数百万行のコードに達し始めると、すべての教科書がばらばらになり始めます。
優れたアーキテクチャーは相互作用の爆発の一部を制御できますが、必然的に、システムがより複雑になるにつれて、自動テストシステムもそれに伴って成長します。
ここから、トレードオフに対処しなければならなくなります。
次のことも決定する必要があります。
コード ベースのどこにテスト ケースを保存しますか?
私は永遠に続けることができますが、私のポイントは次のとおりです。
テストは保守可能である必要があります。
これらの原則については、この MSDN Magazine の記事で取り上げました。この記事は、すべての開発者が読むことが重要だと思います。
私が「良い」単体テストを定義する方法は、次の 3 つのプロパティを持っているかどうかです。
Jay Fields は単体テストの作成について多くの優れたアドバイスを提供しており、最も重要なアドバイスをまとめた投稿があります。そこには、自分の文脈を批判的に考え、そのアドバイスが自分にとって価値があるかどうかを判断する必要があることが書かれています。ここには驚くべき答えがたくさんありますが、状況に応じてどれが最適かを決めるのはあなた次第です。それらを試してみて、臭いがする場合はリファクタリングしてください。
敬具
テストが互いに依存すべきであることを除いて、私は「A TRIP」の回答を支持します!!!
なんで?
DRY - Don't Repeat Yourself - テストにも適用されます。テストの依存関係は、1) セットアップ時間の節約、2) フィクスチャ リソースの節約、3) 失敗の特定に役立ちます。もちろん、テスト フレームワークがファースト クラスの依存関係をサポートしている場合に限ります。そうでなければ、私は認めます、彼らは悪いです。
些細な 2 行の方法でうまくいくと思い込まないでください。ヌル テストの欠落、マイナス記号の配置ミス、微妙なスコープ エラーに悩まされるのを防ぐ唯一の方法は、簡単な単体テストを作成することです。
多くの場合、単体テストはモック オブジェクトまたはモック データに基づいています。私は 3 種類の単体テストを書くのが好きです。
ポイントは、すべての機能をテストできるようにするために、すべてを再生しないようにすることです。
機能テストとパフォーマンス テストの 2 種類のテストについて考え、それらを異なる方法で扱います。
それぞれに異なる入力とメトリックを使用します。テストの種類ごとに異なるソフトウェアを使用する必要がある場合があります。
Roy Osherove の Unit Test Naming standardsで説明されている一貫したテスト命名規則を使用します。特定のテスト ケース クラスの各メソッドには、次の命名スタイル MethodUnderTest_Scenario_ExpectedResult があります。
各セクションはアッパーキャメルケースを使用し、アンダースコアで区切られています。
テストを実行すると、テスト中のメソッドの名前でテストがグループ化されているため、これが役立つことがわかりました。また、他の開発者がテストの意図を理解できるようにするための規則を用意してください。
また、テスト対象のメソッドがオーバーロードされている場合は、メソッド名にパラメーターを追加します。