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)
- 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)
- 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
- lazy-loads the object
- cached across multiple calls in the same example but not across examples
- cleans itself up after it-block
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
let
and/orbefore(:each)
let
is 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.
* 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.