繁体   English   中英

在Finalizer中,NUnit不会因异常而失败

[英]NUnit does not fail on exception in Finalizer

在我们的框架中,有一些关键对象具有文件句柄或WCF客户端连接。 这些对象是IDiposable ,我们有验证代码(抛出异常),以确保它们在不再需要时得到妥善处理。 (仅限调试,以便我们不希望在发布时崩溃)。 这不一定是关机。

除此之外,我们还有运行代码的单元测试,因此如果我们忘记了这些处理,我们就会失败。

问题是 :在.NET 4.5.1上,使用NUnit(2.6.3.13283)运行器(或使用ReSharper或TeamCity)在抛出Finalizer中的此类异常时不会触发测试失败。

奇怪的是 :使用NCrunch (与超过NUnit的也可以),单元测试失败! (在我当地,至少我可以找到这样的遗失处理)

这非常糟糕,因为我们的构建机器(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。

因此,在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正确地做到了,这是一个很好的问题......

终结者的例外情况有所不同,请参阅c#finalizer throw exception?

在.Net早期,他们被忽略了。 在较新版本中,CLR退出时发生致命错误。

引用埃里克·利珀特Eric Lippert) (几乎所有人都知道这一点):

如果要保证所有终结器都已运行,请调用Collect 调用名为WaitForPendingFinalizers的名称。 这将暂停当前线程,直到终结器线程到处清空队列。 如果你想确保那些已完成的对象有回收的内存,那么你将不得不第二次调用Collect [强调补充]

在不同环境中运行时的不一致行为突出了预测GC行为的难度。 有关垃圾收集的更多信息,请参阅Raymond Chen的文章:

或者Eric的博客文章:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM