NUnit – testing失败后清理

我们有一些访问数据库的NUnittesting。 当其中一个失败时,可能会使数据库处于不一致的状态 – 这不是问题,因为我们为每个testing运行重build数据库 – 但是可能导致其他testing在同一运行中失败。

是否有可能检测到其中一个testing失败并执行某种清理?

我们不想在每个testing中都写清理代码,现在我们已经这样做了。 我想在拆解时进行清理,但是只有在testing失败的情况下才能清理,因为清理工作可能会很昂贵。

更新 :澄清 – 我想testing是简单的,不包括任何清理或error handling逻辑。 我也不想在每次testing运行时执行数据库重置 – 只有在testing失败的情况下。 而这个代码可能应该在拆解方法中执行,但是我不知道有什么方法来获取信息,如果testing我们正在从失败或成功拆除。

Update2

[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的文章 – 这是一个很好的起点。 玩过之后,我想出了以下解决scheme:如果夹具中的一个testing失败,则将调用用自定义的CleanupOnError属性装饰的方法。

以下是testing的样子:

  [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); } } } 

但是这里有一个棘手的部分:插件必须放在NUnit运行器旁边的addins目录中。 我被放在TestDriven.NET目录下的NUnit runner旁边:

C:\Program Files\TestDriven.NET 2.0\NUnit\addins

(我创build了addins目录,它不在那里)

编辑另一件事是清理方法需要是static

我一起砍了一个简单的插件,你可以从我的SkyDrive下载源。 您将不得不在相应的位置添加对nunit.framework.dllnunit.core.dllnunit.core.interfaces.dll引用。

一些注意事项:属性类可以放在代码中的任何地方。 我不想将它放在与插件本身相同的程序集中,因为它引用了两个Core NUnit程序集,所以我将它放在了不同的程序集中。 只要记住要改变CleanAddin.cs中的CleanAddin.cs ,如果你决定把它放在其他地方。

希望有所帮助。

从2.5.7版开始,NUnit允许Teardown检测上次testing是否失败。 一个新的TestContext类允许testing访问关于他们自己的信息,包括TestStauts。

有关更多详细信息,请参阅http://nunit.org/?p=releaseNotes&r=2.5.7

 [TearDown] public void TearDown() { if (TestContext.CurrentContext.Result.Status == TestStatus.Failed) { PerformCleanUpFromTest(); } } 

虽然可能会强制nUnit这样做,但这不是最明智的devise,您可以随时在某处设置临时文件,如果该文件存在,请运行清理。

我build议改变你的代码,以便启用数据库事务,并在testing结束时简单地将数据库恢复到原始状态(例如,放弃代表unit testing的事务)。

就在这里。 您可以使用每次testing后拆解的Teardown属性 。 你想在每次testing之前和之后应用你拥有的数据库“重置”脚本,并拆卸和重新设置。

这个属性在TestFixture里面用来提供一套在每个testing方法运行之后执行的通用函数。

更新 :基于对这个问题的评论和更新,我会说你可以使用teardown属性,并使用私有variables来指示方法内容是否应该触发。

虽然,我也看到你不想要任何复杂的逻辑或error handling代码。

鉴于此,我认为一个标准的安装/ 拆卸对你最有效。 如果出现错误并不重要,并且不必具有任何error handling代码。

如果你需要特别清理,因为下一个testing取决于当前testing的成功完成,我build议重新考虑你的testing – 他们可能不应该依赖于对方。

如何使用Try-Catch块,重新抛出捕获的exception?

 try { //Some assertion } catch { CleanUpMethod(); throw; } 

我会像phsr现在所build议的那样,当你负担得起的时候,重构testing,以便他们不必依赖于另一个testing需要的相同数据,甚至更好地抽象数据访问层,并模拟来自该数据库的结果。 这听起来像你的testing是相当昂贵的,你应该做的所有你的查询逻辑的数据库和商业逻辑在你的组合,你并不在乎结果是什么返回。

你也将能够更好地testing你的ExceptionHandling。

另一个select是有一个特殊的function,会抛出你的exception,在testfixture中设置一个开关,说一个exception发生。

 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

到目前为止没有提到的一个select是将testing包装在一个TransactionScope对象中,所以testing从不向数据库提交任何东西会发生什么。

这里有一些技术细节 。 如果你在unit testing和交易范围上进行search,你可能会发现更多(尽pipe如果你打了一个数据库,你确实在做集成testing)。 我过去成功地使用了它。

这种方法很简单,不需要任何清理,并确保testing是孤立的。

编辑 – 我刚刚注意到雷·海耶斯的回答也类似于我的。

它是如何失败? 是否有可能把它放在一个尝试(做testing)/捕获(修复损坏的数据库)/ finally块?

或者你可以调用一个私人方法来解决它,当你检查你的失败情况。

我不是说这是一个好主意,但它应该起作用。 请记住,断言失败只是例外。 另外不要忘记还有一个[TestFixtureTearDown]属性在夹具中的所有testing都运行后只运行一次。

使用这两个事实,你可以写一些东西,如设置一个标志,如果一个testing失败,并检查testing夹具中的标志的价值撕毁。

我不推荐这个,但它会工作。 你并没有真正使用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)
如果testing失败,将执行一些代码。