Chapter 3 is about stubs. The meat of this chapter is on the techniques used to break dependencies.
To use most of these techniques you need to extract an interface and inject a stub into the class under test. The options presented in the book are:
- Receive an interface at the constructor level
- Receive an interface as a property
- Getting a stub just before the method call (using factories)
The first and second options seem to be the ones we all seem to be more used to, and we also agreed on the fact that is good to use the first method when making a dependency required and using the second method ( by properties) when the dependency is optional.
IoC containers are mentioned but there is little or no mention on how to use them when testing.
The third option we didn’t really see the point, it seemed over complicating something that can be simpler, and we couldn’t see the advantage of using this technique, perhaps it is useful in Legacy scenarios? In the book, he (Roy Osherove) mentions that this seems cumbersome at the beginning but it becomes more natural. I find it hard to understand the advantages of this method, I would really appreciate other peoples comments on this.
Then there is some content on Extract and Override, the idea of overriding a virtual method to be able to obtain a result to make the system testable seems to me somewhat more natural, however, I still think there is a lot of investment (code wise) and small return, it also makes your test harder to read.
There is another section of the chapter when it talks about encapsulation, using internals and [internalsVisibleTo] and in general, different ways to open up your code enough to make it testable hiding bits of the API that you don’t consider should be public.
Not the most straight forward chapter to read but plenty to make you think.
Comments on this chapter from David:
Chapter 3 – Using Stubs to break dependencies
Constructor and property injection are very similar and usually involve using an interface to allow you to stub the functionality.
Constructor injection is the most common and is preferred when a dependency is required (most dependencies are).
Property injection is useful when a dependency is optional. Logging is the only example that I can think of. If a logger is set it’s used otherwise no logging takes place but the system still functions as expected.
He does a nice job of discussing pros and cons. Briefly mentions IoC containers but these techniques really come into their own when used in conjunction with a container and it would have been nice to see a bit more said on this. Possibly that the chapter was written early and the IoC stuff added as an afterthought.
3.4.5 Getting a stub just before a method call.
He doesn’t talk about pros and cons in this section as well as he did about constructor injection.
Factory class – Also likely to use an interface but instead of injecting directly into the system under test you inject it into a know location that the SUT then uses to get the dependency. One major drawback with this method (not mentioned in the book) is that it makes the dependencies implicit. With Ctor and property injection, the dependencies are explicitly called out in the interface of the SUT. With implicit dependencies it’s all too easy to miss a dependency that you really should stub during testing. It also makes the test more difficult to follow because you set something up in a place that has no immediate connection to the SUT
Takes a detour into hiding seams using conditional compilation (something I’m really not a big fan of). I thought this section broke up the flow of the discussion and should probably have been kept until the end.
Extract and override
Technique involves subclassing the class under test, in your test project, and overriding a single method so that either a stub (implementing an interface) or a simple value (section 3.5.1) can be injected into the SUT.
In my opinion if you’re injecting a stub you’re probably best off using either ctor or property injection because, as mentioned above, it makes the dependency explicit. I can possibly see some use for this technique if you only want to replace a value or set a flag. Even then I’d have to wonder whether there shouldn’t be some other way to perform the test by first manipulating the state of an object via its public interface.
In the summary he says, “The closer you are to the surface of the object under test [Like with ctor and property injection - DW], the easier your test will be to understand and manage, but you may also be giving up some of your power to manipulate the environment of the object under test.” However he never really goes into much detail of this power. He also talks about using the extract and override technique throughout the book so hopefully we’ll get some more clues later on.