Unit Testing – Breaking Dependencies

In this post I’m going to talk a bit about object dependencies and how to break them so you can isolate your object under test.  In a couple of my previous posts I used a class with one method that demonstrated how to unit test using a mock or fake EF-6 database context.  One detail that I failed to mention (er, I left it out to simplify the sample…yeah, that’s what I did), is that you need to close your connection in the object when your done.  Technically the object I used in the previous examples will work just fine if you open the context and close the context outside your object.  That, however, is ugly code to deal with.  We really want our object to handle the context creation and disposal automatically.  It’s disappointing that we have to destroy all of our “usings” just to break the dependency on the context to begin with.  There’s no need to make it even uglier.

So here’s the object I defined in my last unit testing post:


public class PersonnelPerDepartment
{
    private DepartmentContext _DeptContext;

    public PersonnelPerDepartment(DepartmentContext deptContext)
    {
        _DeptContext = deptContext;
    }

    public int TotalPersonnel()
    {
        var personnelDeptQuery = (
            from in _DeptContext.departments
            join in _DeptContext.people on d.id equals p.department
            select p).ToList();

        return personnelDeptQuery.Count();
    }
}

What I want to do is only pass the context in if it’s used under testing, but make the object keep track of creating and disposing of the context on it’s own if not under test.  So here’s what I’m going to change it to:

public class PersonnelPerDepartment
{
    private DepartmentContext _DeptContext;

    public PersonnelPerDepartment()
    {
        _DeptContext = new DepartmentContext();
    }

    ~PersonnelPerDepartment()
    {
        if (_DeptContext is IDisposable)
        {
            ((IDisposable)_DeptContext).Dispose();
        }
    }

    public PersonnelPerDepartment(DepartmentContext deptContext)
    {
        _DeptContext = deptContext;
    }

    public int TotalPersonnel()
    {
        var personnelDeptQuery = (
            from d in _DeptContext.departments
            join p in _DeptContext.people on d.id equals p.department
            select p).ToList();

        return personnelDeptQuery.Count();
    }

}

If your object has a base class with a dispose method, then you’ll have to something similar to this:

    protected override void Dispose(bool disposing)
    {
        if (_DeptContextis IDisposable)
        {
            ((IDisposable)_DeptContext).Dispose();
        }
        base.Dispose(disposing);

    }

The purpose of this code is to ensure that your dbcontext has been properly disposed of when the object is disposed.  Normally your dbcontext will be disposed at the end of the “using” clause.  We can’t use the “using” clause because we need to inject a fake database context into this object when we unit test it.

Another dependency to watch out for is if the object under test calls another object that uses it’s own database context.  You’ll have to pass the database context variable to the object being called so that it can be faked or mocked under tests as well.  This technique can get ugly if you have many nested or dependent objects.  If you need to fake or mock an object being called from this object, you can use the same pattern as above.  Pass a pointer to the object under question in the constructor.  Call the method when needed and dispose of the object when the current object is disposed of.  Then you can mock the called object and pass it into this object.

Here’s a very simple example:

public class SecondObject
{
    ~SecondObject()
    {
        // dispose allocated stuff here
    }

    public void SomeMethod()
    {
    }
}

public class FirstObject
{
    private SecondObject _secondObject;

    public FirstObject()
    {
        _secondObject = new SecondObject();
    }

    public FirstObject(SecondObject secondObject)
    {
        _secondObject = secondObject;
    }

    ~FirstObject()
    {
        if (_secondObject is IDisposable)
        {
            ((IDisposable)_secondObject).Dispose();
        }
    }

    public void FirstObjectMethod()
    {
        _secondObject.SomeMethod();
    }
}

There is a good book on breaking dependencies.  It’s called “Working Effectively With Legacy Code” and I’ve read it twice.  It has a lot of good examples of difficult dependencies and how to break them for unit testing:


Another good book is “Professional Test Driven Development in C#”:


Summary

I’ve only touched on the subject of breaking dependencies in this blog post.  My intention was to introduce you to the concept and make you aware that it’s a task that needs to be done in order to effectively unit test an object.  if you can’t isolate an object for unit testing, then you can’t properly unit test it.  Eventually, you’ll get used to the pattern and you’ll start to incorporate this technique into your objects before you start unit testing.  Or, if you get really good at this, you’ll write your unit tests first, then create your objects to work under testing and finally making your object work for your real environment (i.e. Test Driven Development).  The ultimate goal is to unit test every behavior that your objects are expected to perform.  Then your unit tests become the documentation of your code and how it was designed to behave in the first place.

 

Leave a Reply