NUnit - テスト失敗後のクリーンアップ
質問
データベースにアクセスする NUnit テストがいくつかあります。そのうちの 1 つが失敗すると、データベースが不整合な状態になる可能性がありますが、テスト実行ごとにデータベースを再構築するため、これは問題ではありませんが、同じ実行で他のテストが失敗する可能性があります。
テストの 1 つが失敗したことを検出し、何らかのクリーンアップを実行することは可能でしょうか?
すべてのテストでクリーンアップ コードを作成する必要はありません。すでにそうしています。クリーンアップにはコストがかかる可能性があるため、テストが失敗した場合のみ、Teardown でクリーンアップを実行したいと考えています。
アップデート:明確にするために、テストはシンプルにして、クリーンアップやエラー処理ロジックを含めないでください。また、テストの実行ごとにデータベースのリセットを実行したくないのは、テストが失敗した場合のみです。そして、このコードはおそらく Teardown メソッドで実行されるはずですが、現在破棄しているテストが失敗したか成功したかの情報を取得する方法がわかりません。
アップデート2:
[Test]
public void MyFailTest()
{
throw new InvalidOperationException();
}
[Test]
public void MySuccessTest()
{
Assert.That(true, Is.True);
}
[TearDown]
public void CleanUpOnError()
{
if (HasLastTestFailed()) CleanUpDatabase();
}
HasLastTestFailed() の実装を探しています
解決
このアイデアに興味を持ったので、少し調べてみました。NUnit にはそのままではこの機能が備わっていませんが、NUnit には拡張性フレームワーク全体が付属しています。見つけました NUnit の拡張に関するこの素晴らしい記事 -それは良い出発点でした。いろいろ試した結果、次の解決策を思いつきました。カスタムで修飾されたメソッド CleanupOnError
フィクスチャ内のテストの 1 つが失敗した場合、属性が呼び出されます。
テストは次のようになります。
[TestFixture]
public class NUnitAddinTest
{
[CleanupOnError]
public static void CleanupOnError()
{
Console.WriteLine("There was an error, cleaning up...");
// perform cleanup logic
}
[Test]
public void Test1_this_test_passes()
{
Console.WriteLine("Hello from Test1");
}
[Test]
public void Test2_this_test_fails()
{
throw new Exception("Test2 failed");
}
[Test]
public void Test3_this_test_passes()
{
Console.WriteLine("Hello from Test3");
}
}
ここで、属性は次のとおりです。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class CleanupOnErrorAttribute : Attribute
{
}
アドインから実行する方法は次のとおりです。
public void RunFinished(TestResult result)
{
if (result.IsFailure)
{
if (_CurrentFixture != null)
{
MethodInfo[] methods = Reflect.GetMethodsWithAttribute(_CurrentFixture.FixtureType,
CleanupAttributeFullName, false);
if (methods == null || methods.Length == 0)
{
return;
}
Reflect.InvokeMethod(methods[0], _CurrentFixture);
}
}
}
しかし、ここが難しい部分です。アドインは次の場所に配置する必要があります addins
NUnit ランナーの隣のディレクトリ。私のものは、TestDriven.NET ディレクトリの NUnit ランナーの隣に配置されました。
C:\Program Files\TestDriven.NET 2.0\NUnit\addins
(私が作成したのは、 addins
ディレクトリにありませんでした)
編集 もう 1 つは、クリーンアップ方法が次のとおりである必要があるということです。 static
!
シンプルなアドインをハックしました。ソースは次からダウンロードできます。 私のスカイドライブ. 。への参照を追加する必要があります nunit.framework.dll
, nunit.core.dll
そして nunit.core.interfaces.dll
適切な場所に。
いくつかのメモ:属性クラスはコード内のどこにでも配置できます。アドイン自体と同じアセンブリに配置したくありませんでした。なぜなら、それは 2 つのものを参照しているからです。 Core
NUnit アセンブリなので、別のアセンブリに配置しました。の行を変更することを忘れないでください。 CleanAddin.cs
, 、他の場所に置くことにした場合。
それが役立つことを願っています。
他のヒント
バージョン2.5.7ので、NUnitのは、最後のテストが失敗した場合ティアダウンを検出することができます。 新しいTestContextクラスはテストがTestStauts含む自分自身についての情報にアクセスすることができます。
詳細については、 http://nunit.org/?p=releaseNotes&rを参照してください。 = 2.5.7 の
[TearDown]
public void TearDown()
{
if (TestContext.CurrentContext.Result.Status == TestStatus.Failed)
{
PerformCleanUpFromTest();
}
}
は、それが最も賢明なデザインではありませんこれを行うにNUnitのを強制することは可能であるかもしれない一方で、あなたはいつもどこかに一時ファイルを設定することができ、そのファイルが存在する場合は、あなたのクリーンアップを実行します。
私は、あなたが使用可能なデータベース・トランザクションを持っているようにコードを変更することをお勧めしますし、試験終了時、単純に(例えば、あなたのユニット・テストを表し、トランザクションを破棄)元の状態にデータベースを戻すでしょう。
はいあります。使用できます 取り壊す 属性 各テストの後に分解されます。データベースの「リセット」スクリプトを適用し、各テストの前後に分解して再セットアップする必要があります。
この属性は、TestFixture内で使用され、各テスト方法が実行された後に実行される共通の関数セットを提供します。
アップデート:コメントと質問の更新に基づいて、ティアダウン属性を使用し、プライベート変数を使用してメソッドのコンテンツを起動するかどうかを示すことができると思います。
ただし、複雑なロジックやエラー処理コードは必要ないこともわかりました。
それを考慮すると、標準の Setup/取り壊す あなたに最適です。エラーが発生しても問題はなく、エラー処理コードを使用する必要もありません。
次のテストが現在のテストの正常な完了に依存するため、特別なクリーンアップが必要な場合は、テストを再検討することをお勧めします。おそらく、テストは相互に依存すべきではありません。
キャッチした例外を再スロー、try-catchブロックを使用してはどう?
try
{
//Some assertion
}
catch
{
CleanUpMethod();
throw;
}
私はphsrは今の示唆とあなたがそれを余裕があるときのように、やる彼らは別のテストニーズやより良い抽象データアクセス層と同じデータに依存してからの結果を模擬する必要はありませんようにテストをリファクタリングしますそのデータベース。それはあなたが本当に結果が返されることが何であるか気にしない、あなたのテストはかなり高価であり、あなたのようなあなたのアセンブリ内のデータベースとビジネスロジック上のすべてのクエリのロジックを実行する必要があり鳴ります。
また、あなたは多くの良いExceptionHandlingテストすることができるようになります。
別のオプションは、例外が発生したと言うtestfixtureにスイッチを設定し、あなたの例外がスローされます特殊な機能を有することがある。
public abstract class CleanOnErrorFixture
{
protected bool threwException = false;
protected void ThrowException(Exception someException)
{
threwException = true;
throw someException;
}
protected bool HasTestFailed()
{
if(threwException)
{
threwException = false; //So that this is reset after each teardown
return true;
}
return false;
}
}
そして、あなたの例を使用します:
[TestFixture]
public class SomeFixture : CleanOnErrorFixture
{
[Test]
public void MyFailTest()
{
ThrowException(new InvalidOperationException());
}
[Test]
public void MySuccessTest()
{
Assert.That(true, Is.True);
}
[TearDown]
public void CleanUpOnError()
{
if (HasLastTestFailed()) CleanUpDatabase();
}
}
ここでの唯一の問題は、スタックトレースがCleanOnErrorFixture
につながるということですこれまで言及されていない1つのオプションは、テストがDBには何もコミットしたことがないと何が起こるかは重要ではありませんので、のTransactionScopeオブジェクトのテストアップをラップすることです。
ここでの技術上のいくつかの詳細。 (あなたがDBをヒットした場合、あなたが本当に統合テストを行っているが)あなたはユニットテストとのTransactionScopeで検索を行う場合は、おそらく多くを見つけることができます。私は過去に成功し、それを使用しました。
このアプローチは、任意のクリーンアップを必要としない、簡単で、テストが隔離されていることを保証します。
私は気づいた編集 - レイ・ヘイズの答えはまた、私のと似ています。
どのようにそれができませんの?それがブロック最終的に/ /キャッチ(壊れたDBを修正)(テストを行う)試しにそれを置くことはできますか?
それとも、あなたはあなたの条件に失敗チェックしたときにそれを修正するためにプライベートメソッドを呼び出すことができます。
私は、これは素晴らしいアイディアだとは言わないよ、それが動作するはずです。 アサーションの失敗は単なる例外であることを忘れないでください。また、一度だけフィクスチャ内のすべてのテストが実行された後に実行される[TestFixtureTearDown]属性もあることを忘れないでください。
あなたはテストが失敗した場合にはフラグを設定し、取り壊すテストフィクスチャ内のフラグの値をチェックするような何かを書くことができ、これら二つの事実を使用します。
私はこれをお勧めしませんが、それが働くだろう。意図したようにあなたは本当にNUnitのを使用していないが、あなたはそれを行うことができます。
[TestFixture]
public class Tests {
private bool testsFailed = false;
[Test]
public void ATest() {
try {
DoSomething();
Assert.AreEqual(....);
} catch {
testFailed = true;
}
}
[TestFixtureTearDown]
public void CleanUp() {
if (testsFailed) {
DoCleanup();
}
}
}
あなたは
で[TearDown]
メソッドを追加することができます
if (TestContext.CurrentContext.Result.Status != TestStatus.Passed)
テストが失敗した場合、一部のコードが実行される。