
681
This approach to introducing Dependency Injection works well when the
code includes only one or two constructors and they have small argument
lists. Constructor Injection is the only approach that works if the DOC is an
active object that creates its own thread of execution during construction;
such behavior would make for Hard-to-Test Code (page 209), and we should
probably consider turning it into a Humble Executable (see Humble Object
on page 695). If we have a large number of dependencies as constructor argu-
ments, we probably need to refactor the code to remove this code smell.
Variation: Setter Injection
As with Constructor Injection, the SUT holds a reference to the DOC as an attri-
bute (fi eld) that is initialized in the constructor. Where it differs is that the attribute
is exposed to the client either as a public attribute or via a “setter” method. When
a test wants to replace the real DOC with a Test Double, it assigns to the exposed
attribute (or calls the setter with) an instance of the Test Double. This approach
works well when constructing the real DOC has no unpleasant side effects and
assuming that nothing can happen automatically between the constructor call and
the point at which the test calls the setter for the property. Setter Injection cannot
be used if the SUT performs any signifi cant processing in the constructor that relies
on the dependency. In that case, we must use Constructor Injection. If constructing
the real DOC has deleterious side effects, we can avoid creating it via the construc-
tor by modifying the SUT to use Lazy Initialization [SBPP] to instantiate the DOC
the fi rst time the SUT needs to use it.
Retrofi tting Dependency Injection
When the SUT does not support any of these options “out of the box,” we may
be able to retrofi t this capability via a Test-Specifi c Subclass. If the actual class
to be used is normally retrieved from confi guration data, this retrieval should be
done by some component other than the SUT and the class then passed to the
SUT using Dependency Injection. Such a use of the Humble Object pattern for
the client or confi guration decouples the SUT from the environment and ensures
that tests do not need to set up some external dependency (the confi guration fi le)
to introduce the Test Double.
Another possibility is to use aspect-oriented programming (AOP) to insert
the Dependency Injection mechanism into the development environment. For
example, we might inject the decision to use the Test Double or inject the test-
specifi c logic—the Test Double—directly into the SUT. I don’t think we have
enough experience with using AOP to call this a pattern just yet.
Dependency
Injection
Dependency Injection