- Classes are more modular, as they depend only on the interface of passed-in dependencies. Class behavior can be changed by swapping out a new component.
- Testing is simplified, since stubs can be substituted for any dependency.
- It's harder to understand how a class works when reading just that class. You may have to track down its invocation to see what kind of components are passed in.
- Is DepedencyClass.expects(:new).returns(stub)) a smell, since we should have injected that stub to the class that uses it instead?
- If a class uses DI, should one only pass in already-instantiated dependencies, or is it okay to let the calling class instantiate them?
- Am I missing any pros or cons?
Here are some thoughts I have after spending a minute reflecting on your points:
I always make dependencies injectable, but providing sensible defaults. For example, if I have a method that does some calculations involving the current time, I instantiate a new object but only if the caller doesn't provide one.
I think providing defaults helps a lot with the point you listed against DI, since you know what kind of object is expected by using the default as an example.
When taking a mockist approach to testing, I'm always facing the situation where I have to mock a dependency which is instantiated inside the object under test. I've reflected on the problem for some time, but never came up with a solution which I'm completely happy with. But I do think that stubbing the constructor of a class is a little odd, and wouldn't use that approach myself. Here's some other approaches that have worked for me:
new
):These are some of the methods I came up with, there are probably some more that I didn't think of. Of course, all of those have pros and cons, and some are more suited to specific situations. Maybe this is a discussion for another gist, but since you touched this point I really wanted to share =)
Cheers,
Andre