NUnit在Finalizer中出现异常时不会失败。
NUnit在Finalizer中出现异常时不会失败。
在我们的框架中,有一些关键对象拥有文件句柄或WCF客户端连接。这些对象实现了IDisposable接口,并且我们有验证代码(使用异常抛出)来确保在不再需要时正确地进行了处理(仅在Debug模式下,以避免在发布时崩溃)。这并不一定发生在程序关闭时。\n除此之外,我们还有单元测试来运行我们的代码,并且我们期望如果我们忘记进行这种处理,测试会失败。\n问题在于:在.NET 4.5.1中,使用NUnit(2.6.3.13283)运行器(或使用ReSharper或TeamCity)时,如果在Finalizer中抛出异常,测试不会失败。\n奇怪的是:使用NCrunch(也是基于NUnit的)运行单元测试会导致测试失败!(至少在本地,我能找到这样的缺失处理)\n这非常糟糕,因为我们的构建机器(TeamCity)无法看到这样的失败,我们以为一切都好!但是在调试模式下运行我们的软件将会崩溃,显示我们忘记了进行处理。\n下面是一个示例,展示了NUnit不会失败的情况:\npublic class ExceptionInFinalizerObject\n{\n ~ExceptionInFinalizerObject()\n {\n //在这里尝试使用\"Assert.Fail\"和抛出异常来确保\n Assert.Fail();\n throw new Exception();\n }\n}\n[TestFixture]\npublic class FinalizerTestFixture\n{\n [Test]\n public void FinalizerTest()\n {\n CreateFinalizerObject();\n GC.Collect();\n GC.WaitForPendingFinalizers();\n }\n public void CreateFinalizerObject()\n {\n //在另一个函数中创建对象,使其超出范围,并使其可以进行垃圾回收\n new ExceptionInFinalizerObject();\n }\n}\n在NUnit运行器中运行此代码,一切都是绿色的。\n要求ReSharper调试此测试将确实进入Finalizer方法。
NUnit does not fail on exception in Finalizer这个问题的出现的原因是在NUnit中,当在Finalizer中出现异常时,NUnit并不会将其视为测试失败。这可能导致开发人员在测试过程中没有意识到Finalizer中的异常,从而导致问题难以发现和解决。
解决这个问题的方法是,在Finalizer中捕获异常,并将其记录下来或进行适当的处理。这样可以确保在测试过程中,如果Finalizer中出现异常,NUnit会将其视为测试失败,从而提醒开发人员注意并解决问题。
下面是一个示例代码,展示了如何在Finalizer中捕获异常并将其记录下来:
public class MyClass { ~MyClass() { try { // Finalizer code here } catch (Exception ex) { Console.WriteLine("Exception in Finalizer: " + ex.Message); throw; } } } [Test] public void TestFinalizer() { // Test code here MyClass obj = new MyClass(); obj = null; // Force garbage collection GC.Collect(); GC.WaitForPendingFinalizers(); // Assertion to check if Finalizer ran without exception Assert.IsFalse(FinalizerExceptionOccurred); }
在上面的代码中,通过在Finalizer中使用try-catch块来捕获异常,并在捕获到异常时将其记录下来。然后,在测试方法中,可以添加断言来检查是否出现了Finalizer中的异常。
通过这种方式,可以确保在NUnit中,如果Finalizer中出现异常,NUnit会将其视为测试失败,并提供相应的错误信息,从而帮助开发人员及时发现和解决问题。
总之,NUnit does not fail on exception in Finalizer这个问题的解决方法是在Finalizer中捕获异常,并在测试方法中添加断言来检查是否出现了Finalizer中的异常。这样可以确保在测试过程中,如果Finalizer中出现异常,NUnit会将其视为测试失败,从而提醒开发人员注意并解决问题。
NUnit does not fail on exception in Finalizer的问题是由于在早期的.Net版本中,对于Finalizer中的异常会被忽略,而在较新的版本中,CLR会以致命错误退出。所以,我们需要了解这些信息,并且需要知道我正在使用的.Net版本(4.5.1)是否会受到影响。
解决这个问题的方法是在Finalizer中捕获异常并进行处理,以避免引起CLR的致命错误。下面是一个例子,展示了如何在Finalizer中捕获异常:
class MyClass { ~MyClass() { try { // 在这里进行资源清理操作 } catch(Exception ex) { // 处理异常,可以将异常信息记录下来或者进行其他操作 } } }
通过在Finalizer中捕获异常并进行处理,我们可以避免NUnit在异常发生时失败,并且可以确保程序的正常退出。
,NUnit在Finalizer中的异常不会导致测试失败的原因是因为在早期的.Net版本中,对于Finalizer中的异常会被忽略。解决这个问题的方法是在Finalizer中捕获异常并进行处理,以避免引起CLR的致命错误。
NUnit在Finalizer中发生异常时不会发生失败的问题。为了解决这个问题,可以通过为所有的TestFixture创建一个基类来实现。在基类中,通过注册一个UnhandledExceptionEventHandler来捕获所有未处理的异常,并在TearDown方法中验证是否有异常被抛出。下面是一个示例的基类代码:
public class BaseTestFixture { private UnhandledExceptionEventHandler _unhandledExceptionHandler; private bool _exceptionWasThrown; [SetUp] public void UnhandledExceptionRegistering() { _exceptionWasThrown = false; _unhandledExceptionHandler = (s, e) => { _exceptionWasThrown = true; }; AppDomain.CurrentDomain.UnhandledException += _unhandledExceptionHandler; } [TearDown] public void VerifyUnhandledExceptionOnFinalizers() { GC.Collect(); GC.WaitForPendingFinalizers(); Assert.IsFalse(_exceptionWasThrown); AppDomain.CurrentDomain.UnhandledException -= _unhandledExceptionHandler; } }
通过使用这段代码,我们可以得知是否有异常抛出,但是无法知道具体是哪个异常。对于我们的需求来说,这已经足够了。如果以后需要修改,可以更新这段代码,或者如果有更好的解决方案,也可以将其作为解决方案。
以上是解决NUnit在Finalizer中异常不会失败的问题的方法。接下来是两个需要覆盖的测试场景的示例:
[TestFixture] public class ThreadExceptionTestFixture : BaseTestFixture { [Test, Ignore("Testing-Testing test: Enable this test to validate that exception in threads are properly caught")] public void ThreadExceptionTest() { var crashingThread = new Thread(CrashInAThread); crashingThread.Start(); crashingThread.Join(500); } private static void CrashInAThread() { throw new Exception(); } [Test, Ignore("Testing-Testing test: Enable this test to validate that exceptions in Finalizers are properly caught")] public void FinalizerTest() { CreateFinalizerObject(); GC.Collect(); GC.WaitForPendingFinalizers(); } public void CreateFinalizerObject() { //Create the object in another function to put it out of scope and make it available for garbage collection new ExceptionInFinalizerObject(); } } public class ExceptionInFinalizerObject { ~ExceptionInFinalizerObject() { throw new Exception(); } }
至于为什么NCrunch可以正确处理这个问题,这是一个好问题...其实,你可以将异常本身存储起来以便后续报告,而不是使用一个布尔标志(_exceptionWasThrown)。