Here's how you set up Rspec 2, for those who have not caught on to let().
describe Friendship do
before :all do
@users = (1..5).collect { Factory(:user) }
end
after :all do
@users.each { |user| user.destroy! }
end
it 'should do something' do
@users.each do |user|
user.should be_valid
end
# Something interesting with @users
end
end
You should be able to replace everything you use @variables for with let(), like this:
describe Friendship doI prefer not to preload everything. The tradeoff is slower specs:
let(:users) { (1..5).collect { Factory(:user) } }
before :all do
users
end
after :all do
users.each { |user| user.destroy! }
end
it 'should do something' do
@users.each do |user|
user.should be_valid
end
# Something interesting with @users
end
end
let() cleans up a lot of your code. But where it really shines comes in combination with two things: (1) rspec 2 will preload everthing under spec/support, and (2) Rails 3's secret weapon, ActiveSupport::Concern. You can factor out your let() declarations and share it across your spec, like so:
describe Friendship do
let(:users) { (1..5).collect { Factory(:user) } }
it 'should do something' do
users.each do |user|
user.should be_valid
end
# Something interesting with @users
end
end
# Put this in spec/support/application.rb
module SpecHelpers
module Application
extend ActiveSupport::Concern
included do
let(:users) { (1..5).collect { Factory(:user) } }
end
end
end
# spec/friendship_spec.rb
# Rspec 2 automatically loads everything in spec/support
require 'spec_helper'
describe Friendship do
include SpecHelpers::Application
it 'should do something' do
users.each do |user|
user.should be_valid
end
# Something interesting with @users
end
end
# spec/comment_spec.rb
require 'spec_helper'
describe Comment do
include SpecHelpers::Application
it 'should do something' do
users.each do |user|
user.should be_valid
end
# Something interesting with @users
end
end
nice post dude.
ReplyDeleteBut I have one doubt. Is let() and before :all are same or similar. Could you clarify my doubt?
I saw on a SO post that you commented on that another commenter said that they didn't prefer this approach because they didn't know where `user` came from, and I tend to agree. However, I do like the idea of moving all of the common setups out of the specs (I do something similar by creating custom Faker modules for each of my models). I think you could make this approach less mysterious by adding another submodule called Users that would contain your let() setup and any other user-specific setup code. Then in your spec you would include include SpecHelpers::Application::Users. This would make it obvious that you are loading up user-related code, making the call to `user` less ambiguous in scope.
ReplyDeleteI'm using rails 3.1 and it's not ActiveSupport::Concerns it's ActiveSupport::Concern with no 's'
ReplyDelete@Spooner I made a mistake. It's ActiveSupport::Concern with no 's' in 3.0 as well. Thanks for catching that.
ReplyDeleteFirst of all in your second example you still use @users in the it block which I think is a typo.
ReplyDeleteMy question is in the later examples do the users get destroyed as is done by the after(:all) in the first two examples? If so can you explain how. If not is that a bug or is it not necessary?