回答:
まず、ソースのプラグインから始めましょう-JUnitを使用したJavaでの実用的なユニットテスト(C#-Nunitを使用したバージョンもありますが、これも持っています。大部分は不可知論です。推奨されます。)
Good TestsはA TRIPである必要があります(頭字語は十分にベタベタしていません。この本のチートシートのプリントアウトがあり、これが正しいことを確認するために抜かなければなりませんでした。)
プロフェッショナル:長期的には、本番と同じかそれ以上のテストコードが存在するため、テストコードについては、優れた設計と同じ標準に従ってください。意図を明らかにする名前を持つよく因数分解されたメソッドクラス、重複なし、適切な名前のテストなど
優れたテストはFastも実行します。実行に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つのアサーションを持つことは、ここで役立ちます。
リファクタリングすると、テストが失敗することはありません。
テストは非常に速く実行する必要があるため、実行をためらうことはありません。
すべてのテストは常に合格する必要があります。非決定的な結果はありません。
単体テストは、実稼働コードと同じように、よく因数分解する必要があります。
@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。
乾杯、
ロブ
テストは本来失敗するはずです。次に、それらをパスさせるコードを書く必要があります。そうしないと、バグがあり常にパスするテストを書くリスクがあります。
前述のPragmatic Unit Testingの本の正しいBICEPの頭字語が好きです。
個人的には、正しい結果(1 + 1は加算関数で2を返す必要があります)を確認し、考えられるすべての境界条件(2つの数値の合計は、add関数の整数の最大値より大きい)であり、ネットワーク障害などのエラー条件を強制します。
優れたテストは保守可能である必要があります。
複雑な環境でこれを行う方法はまだわかりません。
コードベースが数百から数百または数百万のコード行に到達し始めると、すべての教科書がばらばらになり始めます。
優れたアーキテクチャは、相互作用の爆発の一部を制御できますが、必然的に、システムがより複雑になるにつれて、自動テストシステムはそれに伴って成長します。
ここから、トレードオフに対処する必要があります。
また、次のことも決定する必要があります。
コードベースのどこにテストケースを保存しますか?
私は永遠に続けることができますが、私のポイントは次のとおりです。
テストは保守可能である必要があります。
これらの原則については、このMSDNマガジンの記事で少し前から取り上げました。
「良い」ユニットテストを定義する方法は、次の3つのプロパティを持っているかどうかです。
ジェイ・フィールズはユニットテストを書くことについて多くの良いアドバイスをしていて、彼が最も重要なアドバイスを要約する投稿があります。そこであなたはあなたがあなたの文脈について批判的に考えるべきであり、そのアドバイスがあなたにとって価値があるかどうかを判断すべきだと読むでしょう。あなたはここで驚くべき答えをたくさん得ますが、あなたのコンテキストに最適なものを決めるのはあなた次第です。それらを試して、それがあなたに悪臭がする場合はリファクタリングしてください。
敬具
私は「A TRIP」という答えを2番目に挙げていますが、テストは互いに依存するべきです。
どうして?
DRY-あなた自身を繰り返さないでください-テストにも適用されます!テストの依存関係は、1)セットアップ時間の節約、2)フィクスチャリソースの節約、3)障害の特定に役立ちます。もちろん、テストフレームワークがファーストクラスの依存関係をサポートしている場合のみです。そうでなければ、私は認めます、彼らは悪いです。
多くの場合、単体テストはモックオブジェクトまたはモックデータに基づいています。私は3種類の単体テストを書くのが好きです:
ポイントは、すべての機能をテストできるようにするために、すべてを再生しないようにすることです。
Roy Osheroveのユニットテストの命名基準で説明されている一貫したテスト命名規則を使用しています。特定のテストケースクラスの各メソッドには、次の命名スタイルMethodUnderTest_Scenario_ExpectedResultがあります。
各セクションはアッパーキャメルケースを使用し、アンダースコアで区切られています。
テストを実行するときに、テストがテスト対象のメソッドの名前でグループ化されていると便利です。また、他の開発者がテストの意図を理解できるようにする規則があります。
テスト対象のメソッドがオーバーロードされている場合は、メソッド名にパラメーターを追加します。