Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is testability alone justification for dependency injection?

The advantages of DI, as far as I am aware, are:

  • Reduced Dependencies
  • More Reusable Code
  • More Testable Code
  • More Readable Code

Say I have a repository, OrderRepository, which acts as a repository for an Order object generated through a Linq to Sql dbml. I can't make my orders repository generic as it performs mapping between the Linq Order entity and my own Order POCO domain class.

Since the OrderRepository by necessity is dependent on a specific Linq to Sql DataContext, parameter passing of the DataContext can't really be said to make the code reuseable or reduce dependencies in any meaningful way.

It also makes the code harder to read, as to instantiate the repository I now need to write

new OrdersRepository(new MyLinqDataContext())

which additionally is contrary to the main purpose of the repository, that being to abstract/hide the existence of the DataContext from consuming code.

So in general I think this would be a pretty horrible design, but it would give the benefit of facilitating unit testing. Is this enough justification? Or is there a third way? I'd be very interested in hearing opinions.

like image 594
fearofawhackplanet Avatar asked Jun 06 '10 20:06

fearofawhackplanet


People also ask

Is dependency injection only for testing?

Dependency Injection is NOT Just for Testing.

What is the main purpose of using dependency injection?

The goal of the dependency injection technique is to remove this dependency by separating the usage from the creation of the object. This reduces the amount of required boilerplate code and improves flexibility.

What is the major limitation of dependency injection?

Disadvantages of Dependency Injection: Dependency injection creates clients that demand configuration details to be supplied by construction code. This can be difficult when obvious defaults are available. Dependency injection can make code difficult to trace (read) because it separates behaviour from construction.

Which choice is benefit of using dependency injection?

A basic benefit of dependency injection is decreased coupling between classes and their dependencies. By removing a client's knowledge of how its dependencies are implemented, programs become more reusable, testable and maintainable.


2 Answers

Dependency Injection's primary advantage is testing. And you've hit on something that seemed odd to me when I first started adopting Test Driven Development and DI. DI does break encapsulation. Unit tests should test implementation related decisions; as such, you end up exposing details that you wouldn't in a purely encapsulated scenario. Your example is a good one, where if you weren't doing test driven development, you would probably want to encapsulate the data context.

But where you say, Since the OrderRepository by necessity is dependent on a specific Linq to Sql DataContext, I would disagree - we have the same setup and are only dependent on an interface. You have to break that dependency.

Taking your example a step further however, how will you test your repository (or clients of it) without exercising the database? This is one of the core tenets of unit testing - you have to be able to test functionality without interacting with external systems. And nowhere does this matter more than with the database. Dependency Injection is the pattern that makes it possible to break dependencies on sub-systems and layers. Without it, unit tests end up requiring extensive fixture setup, become hard to write, fragile and too damn slow. As a result - you just won't write them.

Taking your example a step farther, you might have

In Unit Tests:

// From your example...

new OrdersRepository(new InMemoryDataContext());

// or...

IOrdersRepository repo = new InMemoryDataContext().OrdersRepository;

and In Production (using an IOC container):

// usually...

Container.Create<IDataContext>().OrdersRepository

// but can be...

Container.Create<IOrdersRepository>();

(If you haven't used an IOC container, they're the glue that makes DI work. Think of it as "make" (or ant) for object graphs...the container builds the dependency graph for you and does all of the heavy lifting for construction). In using an IOC container, you get back the dependency hiding that you mention in your OP. Dependencies are configured and handled by the container as a separate concern - and calling code can just ask for an instance of the interface.

There's a really excellent book that explores these issues in detail. Check out xUnit Test Patterns: Refactoring Test Code, by Mezaros. It's one of those books that takes your software development capabilities to the next level.

like image 103
Rob Avatar answered Oct 31 '22 22:10

Rob


Dependency Injection is just a means to an end. It's a way to enable loose coupling.

like image 31
Mark Seemann Avatar answered Oct 31 '22 23:10

Mark Seemann