views:

177

answers:

3

I have the following class which is a decorator for an IDisposable object (I have omitted the stuff it adds) which itself implements IDisposable using a common pattern:

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

I can easily test that innerDisposable is disposed when Dispose() is called:

[Test]
public void Dispose__DisposesInnerDisposable()
{
    var mockInnerDisposable = new Mock<IDisposable>();

    new DisposableDecorator(mockInnerDisposable.Object).Dispose();

    mockInnerDisposable.Verify(x => x.Dispose());
}

But how do I write a test to make sure innerDisposable does not get disposed by the finalizer? I want to write something like this but it fails, presumably because the finalizer hasn't been called by the GC thread:

[Test]
public void Finalizer__DoesNotDisposeInnerDisposable()
{
    var mockInnerDisposable = new Mock<IDisposable>();

    new DisposableDecorator(mockInnerDisposable.Object);
    GC.Collect();

    mockInnerDisposable.Verify(x => x.Dispose(), Times.Never());
}
+4  A: 

I might be misunderstanding, but:

GC.WaitForPendingFinalizers();

Might do the trick - http://msdn.microsoft.com/en-us/library/system.gc.waitforpendingfinalizers.aspx

Steven Robbins
Damn - I knew that! Thanks for the reminder :)
GraemeF
+7  A: 

When writing unit tests, you should always try to test outside visible behavior, not implementation details. One could argue that supressing finalization is indeed outside visible behavior, but on the other hand, there's probably no way you can (nor should you) mock out the garabage collector.

What you try to make sure in your case, is that a "best-practice" or a coding practice is followed. It should be enforced via a tool that is made for this purpose, such as FxCop.

Johannes Rudolph
Yup. Also, you'll never get 100% coverage for your unit tests. Eventually you just have to have faith that your code is going to work, and if you're competent, it should.
Mike Hanson
I actually had a bug because I'd forgotten to check the `disposing` flag in `Dispose()` so wanted to add a test before I fix it.
GraemeF
A: 

::Post Deleted::

Schammer