在我们的框架中,有一些关键对象具有文件句柄或WCF客户端连接。这些对象是IDiposable
,我们有验证代码(抛出异常),以确保它们在不再需要时得到妥善处理。 (仅限调试,以便我们不希望在发布时崩溃)。这不一定是关机。
除此之外,我们还有运行代码的单元测试,因此如果我们忘记了这些处理,我们就会失败。
问题:在.NET 4.5.1上,使用NUnit(2.6.3.13283)运行程序(或使用ReSharper或TeamCity)在{{1}中出现此类异常时不会触发测试失败扔了。
奇怪的是:使用NCrunch(也超过NUnit),单元测试 DO 失败! (在我当地,至少我可以找到这样的丢失处置)
这很糟糕,因为我们的构建机器(TeamCity)没有看到这样的失败,我们认为一切都很好!但是运行我们的软件(在调试中)确实会崩溃,这表明我们忘记了处理
这是一个显示NUnit不会失败的示例
Finalizer
在NUnit跑步者中运行:一切都是绿色的。 要求ReSharper调试此测试确实会进入Finalizer。
答案 0 :(得分:3)
所以在Eric Lippert的帮助下,我发现当NUnit在另一个线程上时,Exceptions
没有捕获到它。所以终结器线程也是如此。
我尝试在NUnit的设置中找到解决方案,但无济于事。
所以我想出了所有TestFixture
的子类,因此我的所有测试都有一个共同的[SetUp]
和[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正确地做到了,这是一个很好的问题......
答案 1 :(得分:1)
终结者的例外情况有所不同,请参阅c# finalizer throwing exception?。
在早期的.Net中,他们被忽略了。在较新版本中,CLR退出并发生致命错误。
答案 2 :(得分:1)
致quote Eric Lippert(他们对此几乎了解多少):
如果您想保证所有终结器都已运行,请在调用
WaitForPendingFinalizers
后调用名称恰当的Collect
。这将暂停当前线程,直到终结器线程到处清空队列。如果您想确保这些已完成的对象回收了内存,那么您将不得不拨打Collect
第二时间。 [强调补充]
在不同环境中运行时的不一致行为只是强调了预测GC行为的难度。有关垃圾收集的更多信息,请参阅Raymond Chen的文章:
或Eric的博客文章: