TDD Design Starter Kit – Dependency Inversion Principle

In the last episode of the TDD Design Starter Kit, I talked about the need to build cohesive classes, and make the relationships between classes loosely coupled. The specific benefit for TDD is to truly isolate the functionality of a class under the microscope of a unit test. But we’ve done all we can to isolate our classes, and we still have some interaction with dependencies. How then can we test the class in isolation?

Use the Dependency Inversion Principle (DIP) to isolate one class from the actual implementation of its dependencies.



Depend upon Abstractions. Do not depend upon concretions


DIP is certainly not an invention of TDD practitioners, but it is employed frequently for no other reason than to enhance testability.

Take a look at this code that does not employ the Dependency Inversion Principle.


public class MessageProcessor1

{

public void RouteOrder(Order order)

{

string queueName;

if (order.IsComplete)

{

queueName = “Queue.Completed”;

order.CompletionDate = DateTime.Now;

}

else

{

queueName = “Queue.Incomplete”;

this.assignOrder(order);

}



OrderQueue orderQueue = new OrderQueue();

orderQueue.SendOrder(order, queueName);

}



public void assignOrder(Order order)

{

// do something to the order

}

}



public class OrderQueue

{

public void SendOrder(Order order, string queueName)

{

MessageQueue queu = new MessageQueue(queueName);

queu.Send(order);

}

}




So what’s so wrong? The MessageProcessor1 class cannot be tested, or function, without the OrderQueue class being executed.


Not only is MessageProcessor1 strongly coupled to OrderQueue, it is coupled to anything that OrderQueu is dependent on. In this case the MessageProcessor1.RouteOrder() method cannot be tested unless MSMQ is available. More annoyingly, any automated test would need to query MSMQ to get the resulting Order object to compare the expected results.


Now, using DIP we make the MessageProcessor2 class dependent upon an abstracted IOrderRouter interface instead.




public interface IOrderRouter

{

void SendCompletedOrder(Order order);

void SendIncompleteOrder(Order order);

}



public class MessageProcessor2

{

private readonly IOrderRouter _router;



public MessageProcessor2(IOrderRouter router)

{

_router = router;

}



public void RouteOrder(Order order)

{

if (order.IsComplete)

{

order.CompletionDate = DateTime.Now;

_router.SendCompletedOrder(order);

}

else

{

this.assignOrder(order);

_router.SendIncompleteOrder(order);

}

}



public void assignOrder(Order order)

{

// do something to the order

}

}





MessageProcessor2 is only dependent upon an interface, and can use any implementation of the IOrderRouter interface (in theory).


Here’s what a unit test for MessageProcessor2 might look like:


[Test]

public void RouteACompletedOrder()

{

Order order = new Order();

order.IsComplete = false;



DynamicMock routerMock = new DynamicMock(typeof(IOrderRouter));



// SendIncompleteOrder(Order) should be called on the incomplete order

routerMock.Expect(“SendIncompleteOrder”, order);

routerMock.ExpectNoCall(“SendCompletedOrder”, typeof(Order));



IOrderRouter router = (IOrderRouter) routerMock.MockInstance;



MessageProcessor2 processor = new MessageProcessor2(router);

processor.RouteOrder(order);



// Call to the DynamicMock to verify the interaction

routerMock.Verify();

}




Now we have our routing logic decoupled from the delivery mechanism. Besides testability, we have added the ability to substitute other delivery mechanisms like a web service, MQ Series, or simply writing to a file.


Mechanically, there is a couple things of note in the unit test above. I used a dynamic mock object with NMock as a stand-in for the IOrderRouter interface to test the interaction. The IOrderRouter instance was passed into the MessageProcessor2 constructor, not created by MessageProcessor2 itself. This is an example of Dependency Injection (Inversion of Control). I will try to blog on both topics soon.


Rules of Thumb to Use DIP



  1. Anytime your code calls out of the CLR, and maybe just the current AppDomain itself. I would isolate any kind of call to databases, messaging queues, web services, and remoting behind a .NET interface. In ASP.NET or web service applications, always abstract any kind of call to the HttpContext objects. Nothing gets in the way of automated testing like coupling to the ASP.NET runtime. Remember, one of your unofficial design goals is to test as much of the application as possible with your laptop disconnected from the network. Look at the Gateway pattern. As a mild rant, using a Service Oriented Architecture does not guarantee that your actual code is loosely coupled. I’ll expand on that soon.
  2. Singleton’s. Singletons can throw a pretty serious monkey wrench in your unit tests by maintaining state between unit tests. Here is one way to avoid the Singleton issue I stole from some former colleagues that wrote PicoContainer.
  3. Interface points between major subsystems. Aggressively use the GoF Facade pattern to limit the exposure between subsystems, and abstract the interface to allow for easier testing with mocks or stubs.
  4. Extension points. This is a pretty obvious usage, but knowing when to make an abstracted extension point is way, way beyond the scope of a mere blog post.


A great codebase to look at for examples is the CruiseControl.NET application. I think they do a very good job at testing a nasty piece of functionality.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in TDD Starter Kit. Bookmark the permalink. Follow any comments here with the RSS feed for this post.