Here’s an RSpec trick I discovered yesterday. Sometimes when you’re writing a test you want to loop over some precondition data. But if you do a loop inside your test (or spec), then all the cases will be subsumed in a single test method (or “it” block). This means you’ll have the following problems:
- The first case to fail will cause the rest of the cases not to run. It’d be nice to see them all in a single test run.
- You won’t take advantage of RSpec’s cool self-documenting trick of labeling each it block with a full description of the failure, and it’ll be harder to debug which case failed.
- If you’re calling into Rails (e.g. in a View spec), you’ll only be able to call certain methods — especially
render
— once per test method. That means that you simply can’t use a loop inside a method to collapse redundant tests into a single block.
Ruby to the rescue! Instead of looping inside your it block, loop outside your it block.
require 'hpricot'
describe "navbar" do
TABS = ["Home", "Articles", "Comments", "Preferences"]
TABS.each do |tab|
it "selects tab #{tab}" do
assigns[:current_navbar_tab] = tab
render "/shared/_navbar.mab"
doc = Hpricot(response.body)
doc.at("//li[@class=active]/a").inner_html.should == tab
end
end
end
When I mentioned this at standup, Nathan mentioned the eval module… maybe he or someone else can add more detail in a comment?
Note that this technique should be used sparingly. It’s kind of a test smell to have loops, but it’s useful in certain cases… In this example it’s actually different code rendering each separate tab. If we spent a bit more time and extracted a Tab object then we could possibly get away with just unit testing that class and trusting it to render properly on the page for each actual tab.
About the Author