← Back to Upcase

Fast Testing with Rails

(Nathaniel Watts) #1

So, I’ve been working on a project for my new workplace, and because of all the dependencies (which are all necessary - I checked), the test suite itself takes 6-7 seconds just to boot up.

My normal workflow on smaller projects is to have guard running, and then use TDD to drive the design of the application. This works pretty well, because on a smaller application, it only takes 1 second or so to run the test file.

However, when working with this current application - it’s taking 6-7 seconds between each save - which deters me from doing true TDD (I end up doing Test First Design instead - which isn’t the goal).

I’ve watched Gary Bernhardt’s videos at DOS, and this talk by Corey Haines, as well as the recent post on the thoughtbot blog, How We Test Rails Applications.

I realize that the issue of speed is because of including test_helper (or spec_helper), which include the Rails stack and the application’s dependencies.

So - all in all, I guess my question is:

What is the best way to create fast tests when needing to test classes that are dependent on Objects that inherit from ActiveRecord?


  • Models with Associations
  • Service classes that use ActiveRecord Objects

Is that just the pain of testing Rails Models? Or should I be pulling out all business logic into Modules/Classes?

Those classes that are services, should I just be injecting the ActiveRecord object into the initialize function - so that I can send in a stub within the test?



(Ben Orenstein) #2

We pretty much just use spring, and everything is fast enough for our TDD cycles to be non-painful.

1 Like

(Geoff Harcourt) #3

+1 on @benorenstein’s suggestion.

I’ve been using Spring as well, and it’s generally pretty simple to set up and run. I recommend adding a line to your spec_helper to reload spring whenever you edit a factory, as Spring won’t track factory changes automatically.

Having the test environment pre-loaded is 80% of the battle. The other two things to do if your test suite isn’t running fast that I would use the --profile option to find your slowest running specs. Sometimes one feature spec can be responsible for a huge portion of your suite’s slowness.

The other thing I try to do to keep suites fast is to access the database as little as possible. Don’t use create when build or new will do. Unless your test relies on associations, you should be able to avoid the DB in most cases.

With Spring in use, I think the effort to employ workarounds like having an ActiveRecord-less spec helper are overkill and of extremely limited marginal value.


(Nathaniel Watts) #4

Thanks, for the quick responses (both of you) !

Does this mean that, when using spring, you’d recommend not pulling business logic out of the Model Objects as well?


(Geoff Harcourt) #5

Definitely still extract logic out. Smaller classes are desirable not for speed, but for testability and solid design.

1 Like

(Daryll Santos) #6

Just to confirm, are these the steps?

  • Spring to get the 80% in.
  • Use build or build_stubbed as opposed to create
  • Extract stuff that can be removed from AR.

(Ben Orenstein) #7

Yes, except:

See what @geoffharcourt said above.


(Nathaniel Watts) #8

I’ve added spring to the project, and things are much faster - thanks, @benorenstein !

One thing I’ve noticed with spring though (at least, in collaboration with guard), is that it’s finicky with some initializers (ex: an internal engine that is setting some defaults, redis and resque.

As such - there are some tests that are not intermittently failing that pass find when just running rake test:all.

Is there an easy fix for this that I’m missing?


(Geoff Harcourt) #9

You can add the initializers folder to the list of things that Spring watches (I do this with factories), and Spring will restart whenever you edit an initializer.


(Nathaniel Watts) #10

Thanks for the quick response @geoffharcourt!

The crazy thing - is that none of the initializers are being edited when this is happening. It just happens off the bat, and continues as I continue to run tests via guard.