Standup – 2/10/09 – Speeding up RSpec tests and fixtures

Interesting things

After making some changes to our RSpec suite (namely moving to fixture scenarios), we realized almost a 50% improvement on our local dev boxes (generally iMacs with 4G of ram). However the CI box (a Mac Mini) remained at about the same duration. Increasing the InnoDB MySQL settings to the following caused a significant performance boost on our CI box (about 200 seconds down from 350 seconds):

The theory is that our local dev boxes have so much ram that the disk reads are cached while the CI box is swapping out to disk often.

Fixtures in RSpec can be slow due to nested describe blocks. Each time a new describe block is opened, the fixtures have to load. Therefore, if your test suite has many nested describe blocks with only a few tests in each describe, this will cause lots of fixture loading.

One possible solution is to use the underlying database (e.g.. MySQL SAVEPOINT and ROLLBACK TO SAVEPOINT) to save and reload the fixtures. Another possibility is to group your tests in a less-nested fashion.

3 Comments

“Fixtures in RSpec can be slow due to nested describe blocks. Each time a new describe block is opened, the fixtures have to load.”

I don’t think this is actually true. rspec-rails extends rails built-in functionality for dealing with fixtures, which, by default, will wrap every example (test in t/u) in its own transaction. Therefore, if you’re using fixtures, they all get loaded at the beginning of each example.

Nesting *should* have absolutely zero impact on this. Of course, I’m happy to be proven wrong. Do you have some actual benchmarks to prove out your statement?

A better solution, if you still can, is to avoid using Rails fixtures at all costs. They’re opaque and nonobvious, and they cause me to lose a day just about every month to tracking down bizarre errors. Better to use something like Fixjour or FixtureReplacement. Your examples’ intentions will be clearer, your objects will be created like real objects (reflecting your current domain logic), and you’ll only create the records you need for each test.