测试终结器和IDisposable

时间:2009-10-21 00:57:07

标签: .net unit-testing idisposable finalizer

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

public class TestClass : IDisposable {

    public bool HasBeenDisposed {get; private set; }

    public void Dispose() {
        HasBeenDisposed = true;
    }

    ~TestClass() {
        Dispose();
    }
}

请注意我现在不关心Dispose / Finalize的正确实现,因为我想先找到测试它的方法。在此阶段,如果调用Dispose / Finalize ware,则将 HasBeenDisposed 设置为true就足够了。

我写的实际测试看起来像:
更新弱势

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

或我更喜欢的代码(更新后添加):

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

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

  1. GC.WaitForPendingFinalizers()确实暂停线程并在 o 中完成实例,但前提是没有root。为它分配NULL并使用WeakReference在完成后获取它。
  2. o 不包含实例时,会在正确的位置执行Finilize(析构函数)代码。
  3. 那么测试这个的正确方法是什么。我错过了什么?

    我认为变量 o 会阻止GC收集它。
    更新:是的,这是问题所在。不得不改用WeakReference。

3 个答案:

答案 0 :(得分:3)

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

因为在没有引用对象之前不会最终确定对象,所以测试终结行为可能会很棘手。 (您需要对该对象的引用才能对其进行断言!)一种方法是间接执行:让对象在最终确定期间发送某种消息。但这会使完成代码完全出于测试目的而扭曲。您还可以对该对象进行弱引用,这将使其有资格进行最终化,并使其在终结器中自我复活 - 但同样,您不希望它在生产代码中恢复自身。

答案 1 :(得分:0)

如果有对象的本地引用,为什么会收集该对象?

答案 2 :(得分:0)

内存分析器是测试泄漏的最合适方法。

我可以推荐.Net Memory Profiler。