Since learning (and loving) automated testing I have found myself using the dependency injection pattern in almost every project. Is it always appropriate to use this pattern when working with automated testing? Are there any situations were you should avoid using dependency injection?
- Anybody can ask a question
- Anybody can answer
- The best answers are voted up and rise to the top
Basically, dependency injection makes some (usually but not always valid) assumptions about the nature of your objects. If those are wrong, DI may not be the best solution:
Outside of dependency-injection frameworks, dependency injection (via constructor injection or setter injection) is very nearly a zero-sum game: you decrease the coupling between object A and it's dependency B, but now any object that needs an instance of A must now also construct object B.
You've slightly reduced the coupling between A and B, but reduced A's encapsulation, and increased coupling between A and any class that must construct an instance of A, by coupling them to A's dependencies as well.
So dependency injection (without a framework) is about equally harmful as it is helpful.
The extra cost is often easily justifiable, however: if the client code knows more about how to construct the dependency than the object itself does, then dependency injection really does reduce coupling; for example, a Scanner doesn't know much about how to obtain or construct an input stream to parse input from, or what source the client code wants to parse input from, so constructor injection of an input stream is the obvious solution.
Testing is another justification, in order to be able to use mock dependencies. That should mean adding an extra constructor used for testing only that allows dependencies to be injected: if you instead change your constructors to always require dependencies to be injected, suddenly, you have to know about your dependencies' dependencies' dependencies in order to construct your direct dependencies, and you can't get any work done.
It can be helpful, but you should definitely ask yourself for each dependency, is the testing benefit worth the cost, and am I really going to want to mock this dependency while testing?
When a dependency-injection framework is added, and the construction of dependencies is delegated not to client code but instead to the framework, the cost/benefit analysis changes greatly.
In a dependency-injection framework, the tradeoffs are a bit different; what you're losing by injecting a dependency is the ability to know easily what implementation you are relying on, and shifting the responsibility for deciding what dependency you are relying on to some automated resolution process (e.g. if we require an @Inject'ed Foo, there must be something that @Provides Foo, and whose injected dependencies are available), or to some high-level configuration file that prescribes what provider should be used for each resource, or to some hybrid of the two (for example, there may be an automated resolution process for dependencies that can be overridden, if necessary, using a configuration file).
As in constructor injection, I think the advantage of doing so ends up, again, being very similar to the cost of doing so: you don't have to know who is providing the data you rely on, and, if there are multiple potential providers, you don't have to know the preferred order to check for providers in, make sure that every location that needs the data checks for all potential providers, etc., because all of that is handled at a high level by the dependency injection platform.
While I don't personally have a great deal of experience with DI frameworks, my impression is that they provide more benefit than cost when the headache of finding the correct provider of the data or service that you need has a higher cost than the headache, when something fails, of not immediately knowing locally what code provided the bad data that caused a later failure in your code.
In some cases, other patterns that obscure dependencies (e.g. service locators) had already been adopted (and perhaps also proven their worth) when DI frameworks appeared on the scene, and the DI frameworks were adopted because they offered some competitive advantage, such as requiring less boilerplate code, or potentially doing less to obscure the provider of dependency when it becomes necessary to determine what provider is actually in use.
And don't use Service locator as dependency injection alternative, it's anti-pattern, more info: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
When you don't stand to gain anything by making your project maintainable and testable.
Seriously, I love IoC and DI in general, and I'd say that 98% of the time I will use that pattern without fail. It's especially important in a multi-user environment, where you code can be reused again and again by different team members and different projects, as it separates logic from implementation. Logging is a prime example of this, an ILog interface injected into a class is a thousand times more maintainable than simply plugging in your logging framework-du-jour, as you have no guarantee another project will use the same logging framework (if it uses one at all!).
However, there are times when it is not an applicable pattern. For example, functional entry points that are implemented in a static context by a non-overridable initialiser (WebMethods, I'm looking at you, but your Main() method in your Program class is another example) simply cannot have dependencies injected at initialisation time. I'd also go as far as to say that a prototype, or any throw-away investigative piece of code, is also a bad candidate; the benefits of DI are pretty much mid-to-long-term benefits (testability and maintainability), if you are certain that you will throw away the majority of a piece of code within a week or so I would say you gain nothing by isolating dependencies, just spend the time you'd normally spend testing and isolating dependencies getting the code working.
All in all, it's sensible to take a pragmatic approach to any methodology or pattern, as nothing is applicable 100% of the time.
One thing to note is your comment about automated testing: my definition of this is automated functional tests, for example scripted selenium tests if you are in a web context. These are generally completely black-box tests, with no need to know about the inner workings of the code. If you were referring to Unit- or Integration-tests I'd say that the DI pattern is almost always applicable to any project that heavily relies on that kind of white-box testing, since, for example, it allows you to test things like methods that touch the DB without any need for a DB to be present.
An alternative to Dependancy Injection is using a Service Locator. A Service Locator is easier to understand, debug, and makes constructing an object simpler especially if you aren't using a DI framework. Service Locators are a good pattern for managing external static dependancies, for instance a database that you would otherwise have to pass into every object in your data access layer.
When refactoring legacy code, it is often easier to refactor to a Service Locator than to Dependancy Injection. All you do is replace instantiations with service lookups and then fake out the service in your unit test.
However, there are some downsides to the Service Locator. Knowing the depandancies of a class is more difficult because the dependancies are hidden in the class's implementation, not in constructors or setters. And creating two objects that rely on different implementations of the same service is difficult or impossible.
TLDR: If your class has static dependancies or you are refactoring legacy code, a Service Locator is arguably better than DI.