-
Notifications
You must be signed in to change notification settings - Fork 61
Using let and given together
The Given
in RSpec/Given is inspired by RSpec's let
. In fact, it is nearly identical to let
in implementation. Both define a lazy method that can be referenced later in the test.
So, if you have Given
, why would you ever use let
?
There is a difference in intention. Given
says "Here is a named value that is needed for the setup of this specification." Whatever value is specified in the Given clause, that is part of the preconditions of spec.
Here's an example to highlight the difference: In the Semantic Expressions example, the :reduce method returns two items, a new expression and a (possibly) new environment.
describe "Statements" do
describe Assign do
Given(:five) { Number.new(5) }
Given(:stmt) { Assign.new(:x, five) }
describe "#reduce" do
When(:result) { stmt.reduce({}) }
Then { result == [DoNothing.new, {x: five}] }
end
end
end
The result returned by reduce is structured value. It's possible to assert equality on a structured result, but it is difficult to see the errors when they (inevitably) occur.
I like to break up the result from :reduce into parts, called :reduced_statement and :reduced_environment. I could create these named values with Given
, but they aren't really part of the setup for the spec, they are just conveniently named values used in the assertions. Hence I will use let
to specify them.
describe Assign do
Given(:five) { Number.new(5) }
Given(:stmt) { Assign.new(:x, five) }
describe "#reduce" do
When(:result) { stmt.reduce({}) }
let(:reduced_statement) { result[0].inspect }
let(:reduced_environment) { result[1] }
Then { reduced_statement == "«do-nothing»" }
Then { reduced_environment == {x: five} }
end
end
The key thing to remember is that if the named value is part of the setup for the specification, use Given
to convey that meaning. If the named value is merely for convenience, then let
is fine.
Return to Semantic Expression Examples