Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Spec "Helper"

I spent years writing tests for my Rails apps, and every single test file began with this one, seemingly innocuous line:

require 'spec_helper'

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:

require 'path/to/some_poro'

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 ActiveRecord::Base was.

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.

Comments
  1. Stephan says:

    Hi,

    to me it looks like this applies to other programming languages and frameworks, too.

    It’s so easy to get tangled in dependencies (and unnecessary ones at times).
    Explicitly requiring the dependencies is indeed a bit painful, but then it’s (very often) only feeling the pain that causes one to actually change the system.

    Thanks for sharing!

    Stephan

  2. Robbie Clutton says:

    I wonder if you could use the RSpec tags to include requirements for certain types of tests, e.g.

    
    it 'hits the database', :db do
      ...
    end
    

    The :db tag would require ActiveRecord and dependent configuration. That way each test is explicit about it’s dependencies and you might even have a build which runs one tag at a time.

  3. Brian Butz says:

    I feel like the implementation is the appropriate place to require ‘active_record’, not the test itself. Require the thing you are trying to test in your test, and let that require what it needs.

    I think the main reason spec_helper files slow down tests is because they load the Rails environment, which in turn will load every single gem in that environment. It might be an interesting experiment to tag all the gems in a Gemfile with ‘require: false’ and see where the cards fall.

    • Hi Brian,

      I’d normally agree – except that if, e.g., all of the active record models required ActiveRecord (and whatever else the models have going on), it could significantly impact the boot time of the rails app in production or test. Ruby’s require is slow – looking up whether or not something is already required is basically O(n^2)

  4. Francesco says:

    I agree only with roughly half of tests.
    It’s true, for only few tests, loading rails is insanely slow.

    But actually you are testing a rails application, not something different, so you will need to load all dependencies at some point.

    Yea you may “earn” few seconds when you run 2-3 tests with focus: true only, but once you have a bigger code base (and more tests), requiring everything becomes more useful.

    You can still load only few files in spec helper and I do it when I build a gem, but not when I build a rails app where you need 30 files or so.

    Also, the fact that you don’t want to depends on things like devise (or warden), activerecord and such is like not relying on community and the “gem system”, which for me defeats probably the strongest ruby point

  5. Jamie says:

    Matt,

    AFAIK, looking up whether a file has already been required was never quadratic-time. It has been constant-time since 1.9.3 and was linear before that.

    http://www.rubyinside.com/ruby-1-9-3-faster-loading-times-require-4927.html

  6. Tim says:

    I also asked this question on stack overflow a while ago – http://stackoverflow.com/questions/16450916/rspec-require-spec-helper-in-rspec-file

    Clearly the dependencies issue is the main reason for requiring spec_helper in every file – which allows you to require different spec helpers for different types of test.

  7. The practice of isolation is great and it will make you painfully aware of all your dependencies, not least in a Rails app.. I raised this question on Stack Overflow:

    http://stackoverflow.com/questions/22451223/how-do-i-do-fast-unit-testing-in-a-rails-app-without-loading-the-rails-environme

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *