Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Dispose methods be unit tested?

I am using C#. Is it advised to unit test dispose methods? If so why, and how should one test these methods?

like image 275
sbenderli Avatar asked Jul 15 '10 19:07

sbenderli


People also ask

What Cannot be unit tested?

Any component that interacts with an external database, files, or the network cannot be checked as part of unit testing. Unit tests primarily test isolated components during the product's early development phase. Developers using frameworks like Nodejs, Angular, etc., employ Unit Testing Frameworks.

How do you use the dispose method?

The Dispose() methodThe Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object. Finalize override. Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. If the type has no finalizer, the call to GC.

When the Dispose method is called?

It only occurs when there are objects in the Finalization Queue. It only occurs when a garbage collection occurs for Gen2 (which is approx 1 in every 100 collections for a well-written . NET app).

Which attribute is used to denote a class that contains unit tests?

The TestClass attribute denotes a class that contains unit tests. The TestMethod attribute indicates a method is a test method.


2 Answers

Yes, but it might be hard. There are two things that can generally happen in Dispose implementation:

Unmanaged resources are released.

In this case it's pretty hard to verify that the code called, for example, Marshal.Release. A possible solution is to inject an object that can do the disposing and pass a mock to it during testing. Something to this effect:

interface ComObjectReleaser {
    public virtual Release (IntPtr obj) {
       Marshal.Release(obj);
    }
}

class ClassWithComObject : IDisposable {

    public ClassWithComObject (ComObjectReleaser releaser) {
       m_releaser = releaser;
    }

    // Create an int object
    ComObjectReleaser m_releaser;
    int obj = 1;
    IntPtr m_pointer = Marshal.GetIUnknownForObject(obj);

    public void Dispose() {
      m_releaser.Release(m_pointer);
    }
}

//Using MOQ - the best mocking framework :)))
class ClassWithComObjectTest {

    public DisposeShouldReleaseComObject() {
       var releaserMock = new Mock<ComObjectReleaser>();
       var target = new ClassWithComObject(releaserMock);
       target.Dispose();
       releaserMock.Verify(r=>r.Dispose());
    }
}

Other classes' Dispose method is called

The solution to this might not be as simple as above. In most cases, implementation of Dispose is not virtual, so mocking it is hard.

One way is to wrap up those other objects in a mockable wrapper, similar to what System.Web.Abstractions namespace does for HttpContext class - i.e. defines HttpContextBase class with all virtual methods that simply delegates method calls to the real HttpContext class.

For more ideas on how to do something like that have a look at System.IO.Abstractions project.

like image 54
Igor Zevaka Avatar answered Sep 16 '22 15:09

Igor Zevaka


Certainly can't hurt. Client code may try to use an object of your class after it has disposed of it. If your class is composed of other IDisposable objects, you should always be throwing the ObjectDisposedException exception if it is in a state which it is no longer usable.

Of course, you should only be testing the external state of your object. In the example below, I've made the property Disposed external to give me the state.

Consider:

internal class CanBeDisposed : IDisposable
{
    private bool disposed;
    public bool Disposed
    {
        get
        {
            if (!this.disposed)
                return this.disposed;
            throw new ObjectDisposedException("CanBeDisposed");
        }
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                //// Dispose of managed resources.
            }
            //// Dispose of unmanaged resources.
            this.disposed = true;
        }
    }
}

So how I would test this is thus:

CanBeDisposed cbd;

using (cbd = new CanBeDisposed())
{
    Debug.Assert(!cbd.Disposed); // Best not be disposed yet.
}

try
{
    Debug.Assert(cbd.Disposed); // Expecting an exception.
}
catch (Exception ex)
{
    Debug.Assert(ex is ObjectDisposedException); // Better be the right one.
}
like image 31
Jesse C. Slicer Avatar answered Sep 17 '22 15:09

Jesse C. Slicer