Why Rails bugs me, part 1: test/mocks

UPDATE: As I learn more, I'm collecting relevant links at the bottom of this post. Also, I didn't mean to sound anywhere near as negative as I did. My apologies, in particular to David, and I've reworded a few things.

So here's my dilemma. I heart Ruby and have worked with it whenever possible since I learned it in 2002. Then this Heinemeier Hansson guy comes along and builds a web framework on it, markets the tar out of it, it becomes a killer app, and suddenly everybody from Slashdot to Scoble is making noise about Ruby. It's been astonishing and quite wonderful.

So why am I less than enamored with Rails? Maybe I'm just playing the contrarian, as I'm wont to do, but I think I may have some reasons. Since I keep thinking of specific reasons and forgetting them at key moments, I'll try blogging even smallish issues here with whatever useful discussion I can come up with. Maybe then I can connect the dots more meaningfully.

I'll start with a simple one:

test/mocks

I cringed when I saw this introduced. A nice, official-looking directory where I can put code that will screw with my Ruby environment only for tests, and for all tests.

Nooooooo! Why not just start coding with lots of global variables and be done with it?

Well-designed code has minimal dependencies at any one point, and its dependencies are on abstractions rather than implementations. This test/mocks thing blesses the practice of reaching in and hacking on the dependencies of the code under test. This wouldn't be so bad for one or a small group of isolated tests; in fact it's one of the really nice things about Ruby that makes it great for test-driven development. But using test/mocks applies said hacks to all the tests!

Automated tests are worth the most when they can be confidently relied upon: "this test takes A, applies B and C, and I get D." Great. Now say I've changed all the B's in my system, let's call the new version "BB". My production code still uses B, but all of my tests use exclusively BB. How much confidence can a test like that give me when it matters most?

Instead, I should be writing code that can be passed its dependencies so I can use BB only in the tests where it's useful, and have the real McCoy available the rest of the time so my tests catch unexpected integration problems or whatever will happen in the real world where B lurks and my little hacked-up BB ain't.

I wish we could've scored a little chink in the Convention over Configuration armor for a sec back when Jamis wrote up a way to bring ubiquitous dependency injection to Rails. To me, Rails could be so much stronger as a framework with the flexibility and the attention to dependency management Jamis's proposal embodied..

And using the Needle dependency-injection tool this way wouldn't bring about the feared apocalypse of Rails2EE, wherein we do YAML situps to declare ServiceModuleAccessPointTriggers or whatever. It'd just add a spot where we can reach in and change which objects go where for a moment without screwing everything else up.

I really want to know if there's something I can do to help solve this problem. Please fire away with flames, suggestions, questions, or massive groundswell of support in the comments.

Reverie inspired by Stubbing Net::HTTP.get.

Updates



I went back and reread the pages from those few days in November 2004 when all this was shaking out. It looks like it may be possible to make a Needlizing plugin for Rails using Jamis's ideas and hacking the ActionController::Dependencies API.

And now for what might be perhaps the first Rails historical research project (?), wherein I'm trying to figure out who intended what, and what happened to those intentions: