Rails - RSpec - Difference between "let" and "let!"
I understood the difference between let
and let!
with a very simple example. Let me read the doc sentence first, then show the output hands on.
About let doc says :-
...
let
is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked.
I understood the difference with the below example :-
$count = 0describe "let" do let(:count) { $count += 1 } it "returns 1" do expect($count).to eq(1) endend
Lets run it now :-
arup@linux-wzza:~/Ruby> rspec spec/test_spec.rbFFailures: 1) let is not cached across examples Failure/Error: expect($count).to eq(1) expected: 1 got: 0 (compared using ==) # ./spec/test_spec.rb:8:in `block (2 levels) in <top (required)>'Finished in 0.00138 seconds (files took 0.13618 seconds to load)1 example, 1 failureFailed examples:rspec ./spec/test_spec.rb:7 # let is not cached across examplesarup@linux-wzza:~/Ruby>
Why the ERROR ? Because, as doc said, with let
, it is not evaluated until the first time the method it defines is invoked. In the example, we didn't invoke the count
, thus $count
is still 0
, not incremented by 1
.
Now coming to the part let!
. The doc is saying
....You can use let! to force the method's invocation before each example. It means even if you didn't invoke the helper method inside the example, still it will be invoked before your example runs.
Lets test this also :-
Here is the modified code
$count = 0describe "let!" do let!(:count) { $count += 1 } it "returns 1" do expect($count).to eq(1) endend
Lets run this code :-
arup@linux-wzza:~/Ruby> rspec spec/test_spec.rb.Finished in 0.00145 seconds (files took 0.13458 seconds to load)1 example, 0 failures
See, now $count
returns 1
, thus test got passed. It happened as I used let!
, which run before the example run, although we didn't invoke count
inside our example.
This is how let
and let!
differs from each other.
It's not invoked when defined, but rather before each example (and then it's memoized and not invoked again by the example). This way, count will have a value of 1.
Anyway, if you have another example, the before hook is invoked again - all of the following tests pass:
$count = 0describe "let!" do invocation_order = [] let!(:count) do invocation_order << :let! $count += 1 end it "calls the helper method in a before hook" do invocation_order << :example invocation_order.should == [:let!, :example] count.should eq(1) end it "calls the helper method again" do count.should eq(2) endend