問題は、ファイナライズが呼び出されたときにオブジェクトがリソースを破棄するという事実をどのようにテストできるかです。クラスのコード:
public class TestClass : IDisposable {
public bool HasBeenDisposed {get; private set; }
public void Dispose() {
HasBeenDisposed = true;
}
~TestClass() {
Dispose();
}
}
最初にテストする方法を見つけたいので、今は Dispose/Finalize の正しい実装は気にしないことに注意してください。この段階では、Dispose/Finalize ウェアが呼び出された場合に HasBeenDisposedが true に設定されると想定するだけで十分です。
私が書いた実際のテストは次のようになります:
UPDATED WITH 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();
}
または私が好きなコード(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();
}
このテストは失敗します(PASSES AFTER UPDATE)が、次のことを観察します(UPDATED):
- GC.WaitForPendingFinalizers() は、スレッドを中断し、インスタンスをoでファイナライズしますが、ルート化されていない場合のみです。それにNULLを割り当て、WeakReferenceを使用してファイナライズ後に取得しました。
- oがインスタンスを保持していない場合、ファイナライズ (デストラクタ) コードは正しいポイントで実行されます。
それで、これをテストする正しい方法は何ですか。何が恋しいですか?
GCがそれを収集できないのは変数oだと思います。
更新:はい、それは問題です。代わりに WeakReference を使用する必要がありました。