RSpec: before and let
11 Oct 2013Understanding when/why to use before(:all) vs before(:each) vs let vs let!
I’ve learned to use before and let to DRY up RSpec tests, as well as make them more readable. However, I didn’t have a complete grasp of what each of them did. I decided to write some code that helped me understand exactly what each of them do, and when I should be using each one.
before(:all)
before(:all)
FactoryGirl.create(:tag, :permalink => "before-all")
end- runs before each describe/context block
- each insert persists in the database (does not get automatically cleaned up/deleted)

After the above code example is run, there will be 3 records stored in the Tag table.
before(:each)
before(:each)
FactoryGirl.create(:tag, :permalink => "before-each")
end- runs before each example (it-block)
- cleans itself up after each example
- slower than before(:all) if you have a lot of FactoryGirl database inserts

let
let(:tag) { FactoryGirl.create(:tag, :permalink => "let") }- lazy-loads the object
- cached across multiple calls in the same example but not across examples
- cleans itself up after it-block

let-bang
let(:tag2) { FactoryGirl.create(:tag, :permalink => "let-bang") }- runs right away at the beginning of each it-block
- cached across multiple calls in the same example but not across examples
- cleans itself up after it-block

Tips
- Most of the time you should be using
letand/orbefore(:each) letis almost always preferable to reference objects- instance variables spring into existence only when necessary
before(:each)will create all objects even if the example doesn’t use it
before(:all)shouldn’t really be used for variable setup, because it introduces an order dependency between specs- Good use cases: opening a network connection, pre-seeding caches
- If you use
before(:all), think about using correspondingafter(:all)for clean-up before(:each)can be used to instantiate objects to make specs easier to read, if there is a lot of setup involved before an example- If you need records in the db without first referencing an object,
before(:each)seems to be a better choice thanlet! - Don’t mix before and let together, i.e.
before(:each); let(:foo) {}; end * You'll get a warning in RSpec 2 but not RSpec 1: `let` and `subject` declarations are not intended to be called in a `before(:all)` hook, as they exist to define state that is reset between each example, while `before(:all)` exists to define state that is shared across examples in an example group.