RSpec: What is the difference between let and a before block?

kriysna picture kriysna · May 12, 2011 · Viewed 28.6k times · Source

What is difference between let and a before block in RSpec?

And when to use each?

What will be good approach (let or before) in below example?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

I studied this stackoverflow post

But is it good to define let for association stuff like above?

Answer

Jay picture Jay · Mar 26, 2013

People seem to have explained some of the basic ways in which they differ, but left out before(:all) and don't explain exactly why they should be used.

It's my belief that instance variables have no place being used in the vast majority of specs, partly due to the reasons mentioned in this answer, so I won't mention them as an option here.

let blocks

Code within a let block is only executed when referenced, lazy loading this means that ordering of these blocks is irrelevant. This gives you a large amount of power to cut down on repeated setup through your specs.

One (extremely small and contrived) example of this is:

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end

You can see that has_moustache is defined differently in each case, but there's no need to repeat the subject definition. Something important to note is that the last let block defined in the current context will be used. This is good for setting a default to be used for the majority of specs, which can be overwritten if needed.

For instance, checking the return value of calculate_awesome if passed a person model with top_hat set to true, but no moustache would be:

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end

Another thing to note about let blocks, they should not be used if you're searching for something which has been saved to the database (i.e. Library.find_awesome_people(search_criteria)) as they will not be saved to the database unless they have already been referenced. let! or before blocks are what should be used here.

Also, never ever