20

私は次のクラスを持っています。これはオブジェクトのデコレータです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());
}
4

4 に答える 4

15

私は誤解しているかもしれませんが:

GC.WaitForPendingFinalizers();

トリックを行う可能性があります-http ://msdn.microsoft.com/en-us/library/system.gc.waitforpendingfinalizers.aspx

于 2010-05-24T09:11:35.737 に答える
8

単体テストを作成するときは、実装の詳細ではなく、常に外部の目に見える動作をテストするようにしてください。ファイナライズの抑制は確かに目に見える振る舞いの外にあると主張する人もいるかもしれませんが、一方で、ガラベージコレクターをモックアウトする方法はおそらくありません(また、そうすべきではありません)。

自分のケースで確認しようとしているのは、「ベストプラクティス」またはコーディングプラクティスに従っていることです。これは、 FxCopなど、この目的のために作成されたツールを介して実施する必要があります。

于 2010-05-24T09:12:03.583 に答える
2

Appdomainを使用しています(以下のサンプルを参照)。クラスTemporaryFileは、コンストラクターで一時ファイルを作成し、Disposeまたはfinalizer〜TemporaryFile()でそれを削除します。

残念ながら、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));
    }
于 2013-06-09T20:52:58.180 に答える
1

ファイナライズをテストするのは簡単ではありませんが、オブジェクトがガベージコレクションの対象であるかどうかをテストするのは簡単です。

これは弱参照で行うことができます。

テストでは、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");
    }
于 2016-01-20T15:57:41.667 に答える