Reputation: 28392
I am trying to cleanup my specs as they are becoming extremely repetitive.
I have the following spec
describe "Countries API" do
it "should render a country list" do
co1 = Factory(:country)
co2 = Factory(:country)
result = invoke :GetCountryList, "empty_auth"
result.should be_an_instance_of(Api::GetCountryListReply)
result.status.should be_an_instance_of(Api::SoapStatus)
result.status.code.should eql 0
result.status.errors.should be_an_instance_of Array
result.status.errors.length.should eql 0
result.country_list.should be_an_instance_of Array
result.country_list.first.should be_an_instance_of(Api::Country)
result.country_list.should have(2).items
end
it_should_behave_like "All Web Services"
it "should render a non-zero status for an invalid request"
end
The block of code that checks the status will appear in all of my specs for 50-60 APIs. My first thought was to move that to a method and that refactoring certainly makes things much drier as follows :-
def status_should_be_valid(status)
status.should be_an_instance_of(Api::SoapStatus)
status.code.should eql 0
status.errors.should be_an_instance_of Array
status.errors.length.should eql 0
end
describe "Countries API" do
it "should render a country list" do
co1 = Factory(:country)
co2 = Factory(:country)
result = invoke :GetCountryList, "empty_auth"
result.should be_an_instance_of(Api::GetCountryListReply)
status_should_be_valid(result.status)
result.country_list.should be_an_instance_of Array
result.country_list.first.should be_an_instance_of(Api::Country)
result.country_list.should have(2).items
end
end
This works however I can not help feeling that this is not the "right" way to do it and I should be using shared specs, however looking at the method for defining shared specs I can not easily see how I would refactor this example to use a shared spec.
How would I do this with shared specs and without having to re-run the relatively costly block at the beginning namely
co1 = Factory(:country)
co2 = Factory(:country)
result = invoke :GetCountryList, "empty_auth"
Upvotes: 3
Views: 2122
Reputation: 15292
Here's one option, using the new-ish "subject" feature of RSpec. Note that this will run the before :all
block twice, once for each nested "describe" block. If this ends up being too slow, you can flatten things out at the cost of not being able to use the "subject" syntax for the status shared examples (since subject applies to the entire describe block it's used in).
shared_examples_for "valid status" do
it { should be_an_instance_of(Api::SoapStatus) }
its(:code) { should eql(0) }
its(:errors) { should be_an_instance_of(Array) }
its(:errors) { should be_empty }
end
describe "Countries API" do
before :all do
co1 = Factory(:country)
co2 = Factory(:country)
@result = invoke :GetCountryList, "empty_auth"
end
subject { @result }
it { should be_an_instance_of(Api::GetCountryListReply) }
its(:country_list) { should be_an_instance_of (Array) }
it "should have countries in the country list" do
@result.country_list.each {|c| c.should be_an_instance_of(Api::Country)}
end
its(:country_list) { should have(2).items }
describe "result status" do
subject { @result.status }
it_should_behave_like "valid status"
end
end
Upvotes: 3