c# - NUnit 不会因终结器中的异常而失败

标签 c# .net nunit finalizer

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

最重要的是,我们有运行我们代码的单元测试,因此如果我们忘记这样的处置,我们预计它们会失败。

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

奇怪的是:使用NCrunch (也超过了 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 调试此测试确实会进入终结器。

最佳答案

因此,在 Eric Lippert 的帮助下,我发现 Exceptions 在另一个线程上时不会被 NUnit 捕获。因此终结器线程也会发生同样的情况。

我尝试在 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# - NUnit 不会因终结器中的异常而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32114726/

相关文章:

c# - CSV 文件到位图

.net - 使用模式按名称查找 Windows 窗体控件

c# - 将接口(interface)传递给 Startup.cs ConfigureServices 中的另一个服务

c# - 如何正确继承克隆方法?

c# - 如何获得3维数组的横截面c#

c# - 使用通用抽象类型参数和链接的 .net 单元测试

c# - Directory.GetFiles searchPattern 与文档不一致

sql-server-2000 - 我可以在 Nunit 中创建数据库死锁测试吗?

c# - NUnit:如何从非静态方法传递 TestCaseData?

.net - 可以将 nunit 配置为删除每个夹具(或每个测试)的应用程序域