I spent years writing tests for my Rails apps, and every single test file began with this one, seemingly innocuous line:
When I first started writing tests, I had no idea why I wrote this line – other than that’s what the README said to do.
At first glance, what could possibly be wrong with that line? It’s clearly there to help. It helps specs. It says so.
At some point I had to open up that file. Probably because I read another blog post that told me to change something inside it.
And then I read through the code. It configured Rspec, loaded up support files, and loaded Rails (not necessarily in that order).
Well, what’s wrong with that? I was testing Rails apps. Of course it should load Rails.
Some time later, I wrote a test like this:
require 'spec_helper' describe SomePORO do it "does some PORO stuff that doesn't require rails" do #... end end
The test took 10 seconds to run. And that was annoying. But when I read through the test, it yelled at me that it didn’t need rails. So why was I requiring
spec_helper, which required Rails?
I replaced the first line with another simple line:
And now the test ran in a millisecond.
ZOMG my test was so fast!!! SPEED UP ALL THE THINGS!!!
And so I tried. But inevitably I came to an activerecord model. And suddenly my
require 'path/to/some/activerecord' trick no longer worked. I couldn’t run the test, because it had no idea what
So I required it. And then it blew up because it didn’t know what database it was connecting to. So I sorted that out. And then it couldn’t load the models my model depended on. So I required those too. And then it couldn’t load the things those models depended on. Things like devise, and money. All of the sudden, I was requiring 20 different things.
I was eventually able to get the test running… but what a slog. Wouldn’t it make more sense to just
require 'spec_helper' and be done with it?
No. A better question to ask is: why did my object have so many dependencies? Of course, I didn’t ask myself that question at first, but as time went on and I gained more experience I came to the conclusion that I’m sure all developers come to: the more dependencies an object has, the more susceptible it is to changes external to itself, and the less likely it is to ever be reused. In other words, when an object has tons of dependencies, and especially when those dependencies are hard-wired (as opposed to injected), the more brittle the code becomes. It becomes harder to change.
This is the real reason to manually require dependencies in your tests. When I required
spec_helper, I was doing something much worse than slowing down my feedback loop. I was writing brittle code.
When you load Rails, you load everything. And your production code is free to require everything. Manually requiring dependencies in your tests is negative reinforcement, and that’s a good thing. It’s a very visible form of pain, begging you to rethink your object design.
If you’re trying to ramp up on a new codebase, try to convince your pair to let you remove the
require 'spec_helper' lines from the tests, and see how much code you have to manually require. This can be an effective way to learn about a new code base and get a handle on the object graph.
There are times where I
require 'spec_helper': Rails controller tests, for example. Feature specs, of course. Not much else, actually.
About the AuthorMore Content by Matthew Parker