在我们的框架中,有一些具有文件句柄或 WCF 客户端连接的关键对象。这些对象是IDiposable
我们有验证代码(抛出异常)以确保它们在不再需要时得到正确处置。 (仅调试,这样我们就不想在发布时崩溃)。这不一定是在关机时发生的。
最重要的是,我们有运行代码的单元测试,因此如果我们忘记这样的处理,我们预计它们会失败。
问题是:在 .NET 4.5.1 上,使用 NUnit (2.6.3.13283) 运行程序(或使用 ReSharper 或 TeamCity),当出现此类异常时,不会触发测试失败Finalizer
被抛出。
奇怪的是: Using NCrunch http://www.ncrunch.net/(也超过了 NUnit),单元测试DO失败! (对于我来说,至少我可以在本地找到此类丢失的处置)
这非常糟糕,因为我们的构建机器 (TeamCity) 没有看到此类故障,而且我们认为一切都很好!但是运行我们的软件(在调试中)确实会崩溃,这表明我们忘记了一个处置
这是一个显示 NUnit 不会失败的示例
public class ExceptionInFinalizerObject
{
~ExceptionInFinalizerObject()
{
//Tried here both "Assert.Fail" and throwing an exception to be sure
Assert.Fail();
throw new Exception();
}
}
[TestFixture]
public class FinalizerTestFixture
{
[Test]
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();
}
}
在 NUnit 运行器中运行它:一切都是绿色的。
要求 ReSharper 调试此测试确实会进入 Finalizer。
所以在埃里克·利珀特的帮助下,我发现Exceptions
当它们位于另一个线程上时,不会被 NUnit 捕获。所以终结器线程也会发生同样的情况。
我尝试在NUnit的设置中寻找解决方案,但无济于事。
所以我想出了对我所有的子类化TestFixture
,这样就有一个共同的[SetUp]
and [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;
}
}
显然,通过这段代码我只能知道抛出了异常,但不知道是哪个异常。不过对于我的使用来说,已经足够了。如果我稍后更改它,我会尝试更新这个(或者如果有人有更好的解决方案,我很高兴将其设置为解决方案!)
我需要涵盖两个场景,因此我将它们放在这里:
[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 做得很好,这是一个很好的问题......
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)