测试终结器和IDisposable

Dmy*_*iak 5 .net unit-testing idisposable finalizer

问题是,如何在调用finalize时测试对象配置资源的事实。该类的代码:

public class TestClass : IDisposable {

    public bool HasBeenDisposed {get; private set; }

    public void Dispose() {
        HasBeenDisposed = true;
    }

    ~TestClass() {
        Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我现在不关心正确的Dispose / Finalize实现,因为我想首先找到对其进行测试的方法。在此阶段,如果调用了Dispose / Finalize软件,就可以假设将HasBeenDisposed设置为true。

我写的实际测试看起来像:用
WEAKREFERENCE更新

[Test]
public void IsCleanedUpOnGarbadgeCollection() {
    var o = new TestClass();
    o.HasBeenDisposed.Should().Be.False();

    **var weak = new WeakReference(o, true); // true =Track after finalisation
    o = null; // Make eligible for GC**

    GC.Collect(0, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();


    **((TestClass)weak.Target)**.HasBeenDisposed.Should().Be.True();
}
Run Code Online (Sandbox Code Playgroud)

或我更喜欢的代码(ADDED AFTER UPDATE):

[Test]
public void IsCleanedUpOnGarbadgeCollection() {
    WeakReference weak = null;

    // Use action to isolate instance and make them eligible for GC
    // Use WeakReference to track the object after finalisaiton
    Action act = () = {
        var o = new TestClass();
        o.HasBeenDisposed.Should().Be.False();
        weak = new WeakReference(o, true); // True=Track reference AFTER Finalize
    };

    act();

    GC.Collect(0, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();

    // No access to o variable here which forces us to use WeakReference only to avoid error
    ((TestClass)weak.Target).HasBeenDisposed.Should().Be.True();
}
Run Code Online (Sandbox Code Playgroud)

此测试失败(通过后更新),但我观察到以下(UPDATED):

  1. GC.WaitForPendingFinalizers()确实会挂起线程并在o中完成实例化,但前提是未植根。将其分配为NULL,并使用WeakReference在完成后将其获取。
  2. o不保存实例时,在正确的位置执行Finilize(析构函数)代码。

那么正确的测试方法是什么。我想念什么?

我想是变量o阻止了GC收集它。
更新:是的,这是问题。不得不改用WeakReference。

ito*_*son 3

“我想是变量 o 阻止了 GC 收集它。” 正确的。堆栈上存在引用意味着该对象是可访问的,因此不符合收集(和终结)的条件。

由于对象只有在没有引用时才会被终结,因此测试终结行为可能会很棘手。(您需要引用该对象才能对其进行断言!)一种方法是间接执行此操作:让对象在终结期间发送某种消息。但这纯粹出于测试目的而扭曲了最终代码。您还可以保留对该对象的弱引用,这将使其有资格进行终结,并让它在终结器中自行复活 - 但同样,您不希望让它在生产代码中自行复活。