"Quick and dirty hack for RSpec's before(:all)"
November 23, 2009
RSpec is generally nice testing framework. It supports before and after hooks which can be invoked before/after each test case or all test cases. before(:all) is a little confusing though. It runs your "before" block before all "describes" and "contexts", also nested ones. Here's example:
describe User do
before(:all) { puts "preparing for war" }
it "should foo" do
...
end
context "active" do
it "should bar" do
...
end
end
context "inactive" do
it "should baz" do
...
end
end
end
I would expect "preparing for war" to show up once. But before(:all) was called thrice. First, for top level "describe", then two times for "contexts". There were some suggestions to change this behaviour or to add and option to skip call for nested groups but nothing has changed recently. People are even trying some crazy hacks like this.
What I needed was to wipe database for every model spec and every request spec because factory generated records made my build unstable (due to uniqueness validations). After trying few things I ended up with using before(:all) with some condition. First, I've added beforetoplevelgroup_ method to Spec::Runner::Configuration and saved it in spec/beforetoplevel_group.rb:
$_groups = []
class Spec::Runner::Configuration
def before_top_level_group
before(:all) do
top_level_group = self.class.to_s[/^.+ExampleGroup::([^:]+)/, 1]
unless $_groups.any? { |g| top_level_group == g }
$_groups << top_level_group
yield
end
end
end
end
Then in spec_helper I used it like this:
require 'before_top_level_group'
Spec::Runner.configure do |config|
config.before_top_level_group do
# re-migrate db for each top-level group (it usually equals one *_spec.rb file)
DataMapper.auto_migrate!
end
end
Voila! I know it's a dirty hack but it works for me and I'll be using it until RSpec is patched or I switch my testing framework to something else (Bacon looks nice).