From the RSpec documentation:
It is very tempting to use before(:all) and after(:all) for situations in which it is not appropriate. before(:all) shares some (not all) state across multiple examples. This means that the examples become bound together, which is an absolute no-no in testing. You should really only ever use before(:all) to set up things that are global collaborators but not the things that you are describing in the examples.
Well-known conventional wisdom says that different test cases (in spec-speak, “examples”) should not depend on one another for state, should be runnable in any order, etc. I certainly agree with this wisdom in general, but I think there’s one case where this rule should be broken. We’ve been writing a fair number of Selenium RC tests lately for our app, using RSpec to drive Selenium RC. When writing integration tests like this, each example (in test speak, “test method”) is often a very long script with lots of shoulds/asserts in it. We lose the nice descriptive power of small examples with specific, descriptive text, and instead are faced with a choice between vague and high-level example descriptions and really long example descriptions using ugly here documents that can easily fall out of synch with the example code.
Instead, we want to be able to do something like this:
describe "A user customizing a car" do use_chained_examples before(:all) do @model = models(:spiffy) log_in go_to_car_customization_start_page end it "should first be required to select car model" do page_title.should == "Select a model" droplist("model_id").should be_present droplist("model_id").options.should == [ "(Please select a model)", models(:spiffy).name, models(:sporty).name ] droplist("model_id").selected_value.should == "" droplist("model_id").select(@model.id) click_next_button end it "should then be required to select paint color" do page_title.should == "Select #{@model.name} color" droplist("color_id").should be_present # etc. end end
A before(:each) block can be used to reset the page between each example, which we’ve found useful when testing a bunch of validations or something similar. Note too that using chained examples is not the default behavior, and must be explicitly specified by the developer, who should “know what they are doing” if they do this.
Anyway, obviously we did figure out how to make this happen, and after we’ve refined it a bit if people are interested we’ll open source our selenium rspec stuff as a plugin. Note that our selenium specs use a different spec_helper.rb than the rest of our normal specs, so we’re keeping the ability to chain examples out of our standard specs, as conventional wisdom would recommend. Let me know what you think.