繁体   English   中英

如何对终结器进行单元测试?

[英]How do I unit test a finalizer?

我有以下类,它是IDisposable对象的装饰器(我省略了它添加的东西),它本身使用一个通用模式实现IDisposable

public class DisposableDecorator : IDisposable
{
    private readonly IDisposable _innerDisposable;

    public DisposableDecorator(IDisposable innerDisposable)
    {
        _innerDisposable = innerDisposable;
    }

    #region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    #endregion

    ~DisposableDecorator()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
            _innerDisposable.Dispose();
    }
}

我可以轻松测试调用Dispose()时是否Dispose()innerDisposable

[Test]
public void Dispose__DisposesInnerDisposable()
{
    var mockInnerDisposable = new Mock<IDisposable>();

    new DisposableDecorator(mockInnerDisposable.Object).Dispose();

    mockInnerDisposable.Verify(x => x.Dispose());
}

但我怎么写一个测试,以确保innerDisposable 没有得到被终结配置? 我想写这样的东西,但它失败了,大概是因为GC线程没有调用终结器:

[Test]
public void Finalizer__DoesNotDisposeInnerDisposable()
{
    var mockInnerDisposable = new Mock<IDisposable>();

    new DisposableDecorator(mockInnerDisposable.Object);
    GC.Collect();

    mockInnerDisposable.Verify(x => x.Dispose(), Times.Never());
}

我可能会误解,但是:

GC.WaitForPendingFinalizers();

可能会做的 - http://msdn.microsoft.com/en-us/library/system.gc.waitforpendingfinalizers.aspx

编写单元测试时,应始终尝试测试外部可见行为,而不是实现细节。 有人可能会说,压制最终确定确实是在可见行为之外,但另一方面,你可能也不可能(也不应该)嘲笑这个关注收集者。

在您的案例中,您尝试确保的是遵循“最佳实践”或编码实践。 它应该通过为此目的而制作的工具强制执行,例如FxCop

我使用Appdomain(参见下面的示例)。 类TemporaryFile在构造函数中创建临时文件,并在Dispose或finalizer~TemporaryFile()中删除它。

不幸的是, GC.WaitForPendingFinalizers() ; 不能帮我测试终结器。

    [Test]
    public void TestTemporaryFile_without_Dispose()
    {
        const string DOMAIN_NAME = "testDomain";
        const string FILENAME_KEY = "fileName";

        string testRoot = Directory.GetCurrentDirectory();

        AppDomainSetup info = new AppDomainSetup
                                  {
                                      ApplicationBase = testRoot
        };
        AppDomain testDomain = AppDomain.CreateDomain(DOMAIN_NAME, null, info);
        testDomain.DoCallBack(delegate
        {
            TemporaryFile temporaryFile = new TemporaryFile();
            Assert.IsTrue(File.Exists(temporaryFile.FileName));
            AppDomain.CurrentDomain.SetData(FILENAME_KEY, temporaryFile.FileName);
        });
        string createdTemporaryFileName = (string)testDomain.GetData(FILENAME_KEY);
        Assert.IsTrue(File.Exists(createdTemporaryFileName));
        AppDomain.Unload(testDomain);

        Assert.IsFalse(File.Exists(createdTemporaryFileName));
    }

测试finalization并不容易,但测试一个对象是否是垃圾收集主题可能更容易。

这可以通过弱引用来完成。

在测试中,在调用GC.Collect()之前,局部变量超出范围是很重要的。 确保函数范围最简单的方法。

    class Stuff
    {
        ~Stuff()
        {
        }
    }

    WeakReference CreateWithWeakReference<T>(Func<T> factory)
    {
        return new WeakReference(factory());
    }

    [Test]
    public void TestEverythingOutOfScopeIsReleased()
    {
        var tracked = new List<WeakReference>();

        var referer = new List<Stuff>();

        tracked.Add(CreateWithWeakReference(() => { var stuff = new Stuff(); referer.Add(stuff); return stuff; }));

        // Run some code that is expected to release the references
        referer.Clear();

        GC.Collect();

        Assert.IsFalse(tracked.Any(o => o.IsAlive), "All objects should have been released");
    }

    [Test]
    public void TestLocalVariableIsStillInScope()
    {
        var tracked = new List<WeakReference>();

        var referer = new List<Stuff>();

        for (var i = 0; i < 10; i++)
        {
            var stuff = new Stuff();
            tracked.Add(CreateWithWeakReference(() => { referer.Add(stuff); return stuff; }));
        }

        // Run some code that is expected to release the references
        referer.Clear();

        GC.Collect();

        // Following holds because of the stuff variable is still on stack!
        Assert.IsTrue(tracked.Count(o => o.IsAlive) == 1, "Should still have a reference to the last one from the for loop");
    }

暂无
暂无

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

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