First Thoughts on Dependency Injection

I may be a little late to the Dependency Injection (DI) party, but it's something I've been thinking about a lot recently. We're experimenting with writing more isolated unit tests at work and one of the tools we're planning on using is DI.

Here's the basic idea as I see it. When you design software in an object oriented way you end up with an object graph. I define an object graph as the image (mental or otherwise) that defines the relationships between your objects. With DI you take those links between objects and make them more flexible.

For example, let's imagine that object A needs objects B and C to do some task. Object A could just instantiate objects B and C and then use them to perform work, but that means the links between object A and objects B and C are very rigid.

Now, refactor the code so that object A requires objects B and C to be passed in. The links on the object graph are now a little less rigid because as long as an object follows the interface offered by object B it could be passed in instead of B. Object A doesn't care as long as it works the same.

The real value to me is that when testing object A I can be less concerned about objects B and C. For my test I'll assume they work correctly and pass in mocked versions of them that return expected values for their methods. Then I focus on making sure object A works correctly. In a different test I'll test object B. With those two pieces tested I've tested each piece in isolation which is easier to process when writing a test, but I've also tested everything as long as B's interface stays the same.

That's the biggest downside to unit testing in isolation I've seen. You'll also need a functional test that ensures object A and object B interact properly and agree on B's interface.

One of the main criticisms I've seen is that DI is the enemy of encapsulation. Encapsulation is the idea that every object only exposes the things it must to other objects. This criticism feels very valid to me. At the moment I'm giving up that encapsulation for more isolation in testing.

I also like DI for passing in other dependencies. For example, passing in a logging function. Maybe in production you want to write to the syslog. In your tests you may just want to make sure the correct values get sent to the log but you don't want to have your tests check the syslog. One way around this is to have your object take in a logging function and in your tests have that function write to STDOUT or some variable you can check.

While looking around the web on the subject I was surprised to find that it appears to be controversial. Some complain that it's not new idea at all and others that it's actually a bad idea. I was sold on the theory the first time I read about it, but haven't used it a lot in practice. I'm still learning and it's something I'm trying to think about as I write code. It'll be interesting to come back to this post and topic in a few months and see how I'm doing.

Here are a few links to read about the disagreement on DI's usefulness or correctness. The Wikipedia talk page is especially interesting.