Don’t prefer `let!` over `let`
This is an answer to the blog post of Jason Swett about the difference of let
and let!
in RSpec. I felt that it needs some clarification from an RSpec fan and heavy let
-user.
let!(…){…}
is actually identical to before { let(…) { … } }
. So I agree that from your view the control flow is clearer than using let
. But this is not the angle I look upon the matter because the main (and here missing) advantage is the possibility to DRY up your specs by overriding your let
s together with context
like this:
describe 'request XY' do
before do
perform_something(params: { id: id })
end context 'when id is NOT specified' do
let(:id) { nil }
it { is_expected.to be_successful }
end context 'when id is out of range' do
let(:id) { 1_000_000 }
it { is_expected.not_to be_successful }
end context 'when id valid' do
let(:id) { create(:dbobject).id }
it { is_expected.to be_successful }
end
end
You need to get used to this inverted style of reading your tests, but it allows you to reduce the lines of codes immensely (normally only for one cross-cutting concern, but together with shared examples and contexts for even more).
If you agree upon this advantage of let
, it starts getting tricky with let!
and I’d suggest you not to “always use the let!
”. Because you will maneuver yourself into situations where you’re tempted to write code like this:
let!(:logged_in_user) { create(:user) }… do your tests with the logged-in usercontext 'when the user is NOT logged-in' do
let(:logged_in_user) { nil }
…
end
As soon as this happens, your code is doomed. It’s very difficult for a reader to distinguish between let!
and let
if he/she needs to debug the test here. In such cases I prefer to use before
to make it very clear that logged_in_user
is always and in every case initialized.