问题描述
现在的问题是我怎么能测试对象部署资源时,定格被称为事实。在code为类:
公共类识别TestClass:IDisposable的{
公共BOOL HasBeenDisposed {获得;私定; }
公共无效的Dispose(){
HasBeenDisposed = TRUE;
}
〜识别TestClass(){
的Dispose();
}
}
请注意我不关心正确执行处置/敲定刚才,我想找到先行先试的方式。在这个阶段,足以承担的的 HasBeenDisposed 的将被设置为true,如果处置/的Finalize洁具调用。
在实际测试中,我写的样子:
更新了WeakReference
[测试]
公共无效IsCleanedUpOnGarbadgeCollection(){
变种O =新的识别TestClass();
。o.HasBeenDisposed.Should()Be.False();
** VAR弱=新的WeakReference(O,真正的);定稿后//真=跑道
O = NULL; //使符合GC **
所以GC.Collect(0,GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
**((TestClass中)weak.Target)** HasBeenDisposed.Should()Be.True()。
}
或code我更喜欢(更新后添加):
[测试]
公共无效IsCleanedUpOnGarbadgeCollection(){
WeakReference的弱= NULL;
//用行动来隔离实例,并使其符合GC
//使用WeakReference的跟踪finalisaiton后的对象
动作ACT =()= {
变种O =新的识别TestClass();
。o.HasBeenDisposed.Should()Be.False();
弱=新的WeakReference(O,真正的); //真=轨道的Finalize后参考
};
法案();
所以GC.Collect(0,GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
//没有获得O变量这里这迫使我们使用的WeakReference只是为了避免错误
((TestClass中)weak.Target).HasBeenDisposed.Should()Be.True()。
}
此测试失败(过后更新),但我注意到以下(更新时间:):
- GC.WaitForPendingFinalizers()不挂起线程和交割情况下的的 0 的,但只有在没有扎根。赋值为null它和使用的WeakReference得到它定稿后。
- Finilize(析构函数)code在正确的点执行时在的 0 的不成立的实例。
那么,什么是这个测试的正确方法。我怎么会错过?
我想这是可变的的 0 的是prevents从收集它的GC。
更新:是的,它是问题。不得不使用的WeakReference代替。
我想这是变量o从它集合了prevents GC。正确。叠上的参考的存在意味着该对象是可达的,因此不符合回收(和定稿)。
由于对象将不会被最终确定,直到没有对它的引用,测试定稿行为很可能会非常棘手。 (您需要以使它断言引用的对象!)的一种方法是间接的做到这一点:有对象终结期间发送某种信息。但是,这扭曲了定稿code纯粹是为了测试目的。你可以举起弱引用的对象,这将使它符合定稿,并让它复活自己在finaliser - 但你又不想让它复活本身在生产code 。 P>
The question is how can I test the fact that object disposes resources when finalise is called.The code for the class:
public class TestClass : IDisposable {
public bool HasBeenDisposed {get; private set; }
public void Dispose() {
HasBeenDisposed = true;
}
~TestClass() {
Dispose();
}
}
Please note that I don't care about the correct implementation of Dispose/Finalize just now as I want to find the way to test it first. At this stage it is enough to assume the HasBeenDisposed will be set to true if Dispose/Finalize ware called.
The actual test I wrote looks like:
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();
}
or the code I like better (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();
}
This test fails (PASSES AFTER UPDATE) but I observe following (UPDATED):
- GC.WaitForPendingFinalizers() does suspend the thread and finalises instance in o, but only if is not rooted. Assigned NULL to it and used WeakReference to get it AFTER finalisation.
- Finilize (destructor) code is executed at correct point when the o does not hold the instance.
So what is the correct way of testing this. What do I miss?
I suppose it is the variable o that prevents GC from collection it.
UPDATE: Yes it is the issue. Had to use WeakReference instead.
"I suppose it is the variable o that prevents GC from collection it." Correct. The existence of a reference on the stack means the object is reachable, and therefore not eligible for collection (and finalisation).
Because an object will not be finalised until there are no references to it, testing finalisation behaviour is likely to be tricky. (You need a reference to the object in order to make assertions about it!) One way is to do it indirectly: have the object send some sort of message during finalisation. But this distorts the finalisation code purely for test purposes. You could also hold a weak reference to the object, which would make it eligible for finalisation, and have it resurrect itself in the finaliser -- but again you don't want to have it resurrect itself in production code.
这篇关于测试终结和IDisposable接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!