Tilting at Windmills

by Jacob 15. August 2007 06:59

Windmill I’ve been giving poor Nate Kohari a hard time over at Discord & Rhyme. He has been very patient in Defending Dependency Injection. His attitude stands in sharp contrast to Ayende Rahien's post today about testing Linq for SQL. Ayende’s snide "(Best Practices, anyone?)" is exactly the attitude I lamented in my original post on Dependency Injection when I said

I do wish that people would admit that DI doesn’t have compelling applicability outside of Unit Testing, however. I’m reading articles and discussions lately that seem to take the superiority of DI for granted. And I’ve read mock object examples that seem a little bit condescending about dependency injection—as if your projects should already incorporate this technique.

I find myself reflecting more and more on how liberating TypeMock is and come back to wondering again if Dependency Injection would exist as a pattern at all if it weren’t for unit testing.

My hypothesis (entirely untestable, so technically a mere speculation) is that what happened is that in order to achieve 100% code coverage in their unit tests with methods that read databases or call web services or have other potentially nasty dependencies, someone came up with the neat idea to invert control of those dependencies so that unit tests could feed in an abstracted object that fakes those calls and the real logic of the methods could be tested without bothering the poor database (or whatever). That’s a noticeable, pervasive, broad architectural change, though, especially if it is only used for unit testing, so in order to defend their idea, developers began looking for additional justification for this pattern. Since nobody but Microsoft uses providers (and that really only in Asp.Net) and software factories are so last year, they cannibalized the strengths of both and pushed them down to the object level and called it done. I’m not saying they did so maliciously or dishonestly. I’m just saying that they needed further justification and that the justifications they chose are already served by applying other useful patterns.

Seriously, with providers that allow you to contextualize defined sub-systems and something that allowed you to Mock objects in testing without having to feed those objects to the tested class, can you think of a good reason to use Dependency Injection? Any dependency that needs to be altered more frequently than a contextual provider framework would allow needs to be exposed for access by calling objects anyway, I’m thinking.

This thinking was crystallized as I explored some of the DI frameworks referenced by Nate. As I watched Kevin Bourillion and Bob Lee explain their Guice for Java framework (a DI framework created by Google), I was struck by the number of references to testing. From that presentation alone, it looks like the main impetus for Guice was unit testing and the other benefits described would have been as easily satisfied with a provider framework.

Come to that, it could be that solid DI frameworks like Guice and Castle Windsor have a niche when it comes to making providers easy to create, control, and use.

Still and all, I wonder what would have happened if they had invented something like a Java version of TypeMock for their mock objects first.

Tags: , ,

Programming

Comments


August 17. 2007 19:09
Bob Lee
I assume .NET providers are like service locators.

First: TypeMock. You can already do similar stuff with AspectJ (which I assume partially inspired TypeMock), but mocking an interface is a lot cleaner and more type safe and maintainable than mocking static methods.

Dependency injection has a number of benefits compared to factories and service locators. With Guice, we think of @Inject as the new "new". You no longer have to decide up front whether to use "new" or a factory or provider any more. With Guice, you can just @Inject something from the beginning (which is almost as concise as using "new") and come back later and configure the dependency externally without changing the original code. This means you can change your code in 1 place instead of N places.

The other nice thing about @Inject vs. new is, you can still have a dependency on the concrete class (if you don't want the level of indirection an interface gives you), but you don't have to depend on everything that concrete class depends on.

I also like that the dependencies are expressed in the API with dependency injection. This doesn't necessarily mean they're exposed in the published API--you can make your constructor package-private for example and Guice will still be able to call it, but you don't have to dig into or run code to find all of its deps.

Compared to providers/service locators, Guice enables much more type safety and up front checking. For example, Guice can tell you at startup that you forgot to configure a dependency whereas you don't find that out with a service locator until you try to run your code.

The talk I gave with Kevin explicitly assumes that you're already sold on DI. If you want more of an introduction or sales pitch, try this talk:

crazybob.org/.../...tion-to-guice-video-redux.html


August 17. 2007 23:48
Jacob
Woah. I'm flattered with this specific response from one of the Guice guys. Thanks.

Don't let the name fool you--TypeMock can handle classes (static and dynamic) as well interfaces just fine. TypeMock's strength is in being able to make your mock substitutions in called methods without feeding the dependency to the object you are testing. I seriously need to make a post giving examples of both just to point out this difference because a lot of people assume that TypeMock is like any other mocking framework and it's not.

Providers aren't really a service locator. Providers are simply an interface definition that is hooked up to a specific type at runtime either via configuration file or programmatically. It achieves loose coupling of your objects, but doesn't cross-link as gracefully as DI (naturally as dependencies aren't injected, just gracefully abstracted).

What that means is that more extensive use of providers would get you part of what DI gives you in terms of looser coupling, but not all the way because DI has explicit dependency linkage which allows you to override/bind/inject things more freely.

Nate and I have discussed it rather thoroughly in the comments of kohari.org/.../">his post if you want to see our more detailed back and forth.

To summarize my personal take so far: DI's main advantage is looser coupling with a robust toolset and better-analyzed architecture space. If I work on projects that are big enough where coupling becomes a serious maintenance concern, I'll be grateful for the work done on DI. That doesn't change the fact that most of the DI I've seen is driven by the testing needs and not the loose coupling needs.

That's too bad because it means that the barrier to entry for complete code coverage in your unit testing is an architecture with non-trivial overhead (learning curve as well as framework research and implementation). I think we'd be much further along in unit testing penetration if we had concentrated on solving the mock object problem without DI instead of trying to hit people over the head convincing them that even their small projects need it because it's "Best Practices".


August 19. 2007 13:08
Jacob,
You have hit the nail on the head with this post.
"The emperor is naked" and you see it.

Take a look at what people are asking for on the xxMocks forums and the yahoo test-driven group and you will see how every problem that the tool can't solve is actually a design smell .
Pragmatic development teams don't believe this anymore.

www.elilopian.com/.../


August 20. 2007 06:18
Mark Seemann
I must admit that I don't agree with either this post, or the former you made on DI.

DI is basically a technique used to implement componentalized (n-layer) architecture. Software architects has been agreeing that this is a good goal long before there was TDD.

Since I've had to explain this too many times, I ended up writing this article on why isolation is important: blogs.msdn.com/.../ReasonsForIsolation.aspx. Please note that I'm not talking about unit testing until the penultimate sentence ;)

And just because you use DI doesn't mean that you have to use a big and complex DI framework, as this post demonstrates: blogs.msdn.com/.../...ependencyConfiguration.aspx.


August 21. 2007 17:57
Colin Jack
Good post.

I'm in an odd position, I'm completely sold on the decoupling idea and in many cases sold on the usefulness of using TDD to drive that. I can completely see how it can help and I'm also completely sold on the theory behind it (Robert Martin in particular is a required read).

However when the decoupling is just being put in for testing, potentially complicating your design/architecture for little gain, well then I think it gets tricky.

To me you have to look at what you're doing and think "is this what I really want". If the answer is no then TypeMock is a good option. However if the answer is yes then you have to look at IoC or a Service Locator and can then use any mocking tool.


August 23. 2007 06:26
Pat Maddox
<q>Any dependency that needs to be altered more frequently than a contextual provider framework would allow needs to be exposed for access by calling objects anyway, I’m thinking.</q>

<p>Isn't that just setter-based DI instead of constructor-based? (assuming you let the caller set a completely new implementation)</p>


 Mark Lovik 
September 28. 2007 18:43
Mark Lovik
I recently worked on a project that depended on dependency injection for different reasons.  

The application was a large C++ telematics application where the cost of remote update is high.  Objects needed to be decoupled, and dependencies needed to be managed and determined at startup.  So object could be independently changed in the system with automatic change control.

Dependency injection made the project much easier than alternative approaches. (It also helped for testing).


 Kevin Wong 
December 9. 2007 03:31
Kevin Wong
I might be wrong, but I believe the history of DI in the mainstream began with the Java framework Spring, whose goal was not to ease unit testing (though the Interface21/SpringSource guys certainly valued it), but to ease enterprise development.  So, easy on the conspiracy theories.


June 12. 2009 10:19
learning software
sometimes you can get away with it but it all really just depends on how much you need it

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading



Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2009 Scruffy-looking Cat Herder