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

问题描述 投票:0回答:5

我有以下类,它是

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();
    }
}

我可以轻松测试当调用

innerDisposable
Dispose()
是否被释放:

[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()); }


c# .net unit-testing idisposable finalizer
5个回答
16
投票

GC.WaitForPendingFinalizers();

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


7
投票

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

FxCop


2
投票

不幸的是,

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)); }



2
投票

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

在测试中,在调用 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"); }



0
投票

var myObj = new MyType(); var finalizer = typeof(MyType).GetMethod("Finalize", BindingFlags.Instance | BindingFlags.NonPublic); finalizer.Invoke(myObj, null);

© www.soinside.com 2019 - 2024. All rights reserved.