Writing Testable Objects

Preface

One of the main places where we run into problems with code being hard to test is when we have utility classes "baked" inside the class we are trying to test and so we can't reach them.  For example, let's say we have utility class that gets a group of numbers.

public class NumberFactory
{
    public IEnumerable<Int32> GetNumber()
    {
        return Enumerable.Range(0, 10);
    }
}

Let's say we implement the "Consumer" class as in the code below:

public class Consumer
{
    private NumberFactory m_utility = new NumberFactory();

    public Int32 GetCount()
    {
        return m_utility.GetNumbers().Count();
    }

    public Int32 LastNumber()
    {
        return m_utility.GetNumbers().Last();
    }
}

We now have a problem when unit testing because our "Consumer" class is dependent on and at the mercy of the "NumberFactory" utility class baked inside it.  It is difficult to test the code living directly in our "Consumer" class because it's behavior is dependant on our "NumberFactory"  it is difficult to write unit tests that will truly isolate and test only the behavior of the "Consumer" class independently.

What' s the solution?

Dependency Injection and Inversion of Control

This fancy sounding pattern is really quite simple. We are basically agreeing that all utility classes that our main class depends on will not be instantiated inside a constructor like the "NumberFactory" class in the above code sample.  Rather, we will provide a way for the more granular functionality be "injected" into our main class somehow so we have some hooks to control the composition of our class which will make it more flexible.  This flexibility has many benefits, one of which is that our class will be easier to test.

Depending on the way we choose to "inject" the more granular utility class we will have a bit different behavior.  Properties are really just methods in disguise, so if we use a property or member method to "inject" our utility class we will have what is called "setter injection".  There are a couple problems using this approach which I'll cover in a bit (see if you can figure it out before we get there).

Here is a sample of our class re-worked to expose a property just so we can inject a more granular class into our "Consumer" using the "Utility" property:

public class Consumer2
{
    private IEnumerable<Int32>
        m_utility =
            new NumberFactory()
            .GetNumbers();

    public IEnumerable<Int32> Utility
    {
        get { return m_utility; }
        set { m_utility = value; }
    }

    public Int32 GetCount()
    {
        return m_utility.Count();
    }

    public Int32 LastNumber()
    {
        return m_utility.Last();
    }
}

Now we have a way to write unit tests and can plug-in some predictable behavior for our underlying granular component in order reach the two methods exposed on our class:

[TestMethod()]
public void LastNumberTest()
{
    Consumer2 target = new Consumer2();

    // set using dependency injection
    target.Utility = new Int32[] { 0, 1 };

    int
        expected = 1,
        actual = target.LastNumber();

    Assert.AreEqual(expected, actual);
}

[TestMethod()]
public void GetCountTest()
{
    Consumer2 target = new Consumer2();

    // set using dependency injection
    target.Utility = new Int32[] { 0, 1 };

    int
        expected = 2,
        actual = target.GetCount();

    Assert.AreEqual(expected, actual);
}

The good thing about this is now our class is testable but there are a couple of really bad side-effects:

(1) We have changed the surface are of a business object to expose something that has nothing to do with the actual responsibility of the class.  This is very bad design.  We have modified the way the class could possibly behave solely to make it more testable and we have effectively broken the single responsibility principle.

(2) Business or domain requirements are no longer the driver in our architecture, testing is.  Not only that, we have higher object model complexity just for testing (complexity is the enemy of maintainability... we should avoid it at all costs).

(3) As a result of our now "broken" architecture, with our additional hook (the injection setter property) we, or another developer who does not understand why the property is exposed, could inadvertently reset the behavior at runtime essentially breaking our project. 

(4) If the class ever has to change (and it probably will) there is a risk of the initialization of our member variable being removed, essentially breaking our object and requiring some more complex instantiation where we will need to set the value inside a factory class or a builder method, adding more complexity to our code.

A Better Way....

The "injection" can be on our constructor method, in which case we have a constructor dependency injection pattern (which is the one I use), because we minimize the change to the surface are of our class just for testing and are guaranteed to have the property set.

Here is the same class with construction injection exposed. Notice we have one constructor where we can inject the appropriate behavior.  We also have a second constructor that provides the default behavior to inject if the consumer chooses to use the default constructor to instantiate our class.

public class Consumer3
{

    // constructor exposed for dependency injection
    public Consumer3(IEnumerable<Int32> utility)
    {
        m_utility = utility;
    }

    // provide a default implementation to inject.
    public Consumer3():this(new NumberFactory().GetNumbers()){}

    private IEnumerable<Int32> m_utility;

    public Int32 GetCount()
    {
        return m_utility.Count();
    }

    public Int32 LastNumber()
    {
        return m_utility.Last();
    }
}

And here are the tests we can use to get coverage of our two methods.

[TestMethod()]
public void LastNumberTest()
{
    Consumer3 target = new Consumer3(new Int32[]{0,1});

    int
        expected = 1,
        actual = target.LastNumber();

    Assert.AreEqual(expected, actual);
}

[TestMethod()]
public void GetCountTest()
{
    Consumer3 target = new Consumer3(new Int32[] { 0, 1 });

    int
        expected = 2,
        actual = target.GetCount();

    Assert.AreEqual(expected, actual);
}

Notice how with dependency injection, we have impacted the surface are of our code as little as possible (we don't have the extra property) and have kept complexity to a minimum which means our class will be much easier to maintain over the long run.

Testing is crucial to building reliable and maintainable software but we should try to make sure we write code that is as simple as possible and have classes with one responsibility.  When we use setter injection we have classes that expose hooks to perform two functions (1) whatever business requirement is there and (2) testability.  This is bad design and is a slippery slope, so if we want our classes to be testable we should have that impact the surface are as minimally as possible.  The best way to accomplish this is through constructor dependency injection. 

Until next time,
Happy Coding

If you want to dig in further I'd recommend reading Martin Fowler's article on the subject:

http://www.martinfowler.com/articles/injection.html


Similar Articles