Reputation: 29389
In RSpec, one of the techniques for providing a value to a shared example is to use let
to define a variable used by that example, as described in this documentation.
Currently, the values passed into formal parameters take precedence over the any values established by let
, as shown in the following example:
shared_examples_for "foo" do |x1|
specify {puts [x1, x2, x3].inspect}
end
describe "" do
let(:x1) {3}
let(:x2) {3}
let(:x3) {3}
it_behaves_like "foo", 1 do
let(:x1) {2}
let(:x2) {2}
end
end
# => [1, 2, 3]
My question is whether it would be possible/reasonable/desirable to change the RSpec semantics such that the variables set by let
inside of the passed block would take precedence over the passed parameters and the above would output [2, 2, 3]
. This would allow "simple" values to be passed in as parameters and more complicated values to be set via a block.
Upvotes: 5
Views: 5040
Reputation: 21800
use
let
to define a variable
let
does not define variables; it defines a memoized helper method. The distinction is important, as ruby treats variables and methods differently. Specifically, local variables (e.g. the x1
block arg in your shared_examples_for
block) always take precedence over methods of the same name, unless you use self.x1
to make it explicit that you are sending a message.
So, to answer your question: no, what you're asking is not possible. I don't think it's desirable, either; having local variables always "win" when there is a name collision is super important for being able to reason about your code. Consider the repurcssions if that was not the case. Let's say you started with this code:
# in superclass.rb
class Superclass
end
# in subclass.rb
class Subclass < Superclass
def do_something(name)
# do something with the `name` variable
end
end
At some future point, Superclass
is updated to have a name
method:
class Superclass
def name
"superclass name"
end
end
If ruby did not give precedence to local variables over methods, the Subclass#do_something
method would get broken by an unrelated change to Superclass
. The fact that local variables always take precedence means that you can more easily reason about what code does without worrying about changing in some distant code suddenly causing a particular identifier to be re-bound to a method rather than a local variable.
Upvotes: 5
Reputation: 3809
I would use context
s if I just wanted to change the context a bit with new let
s.
You can use context
blocks inside or outside describe
blocks. The are essentially the same but create a way for you to semantically organize your specs a differently (IMO better)
describe "adding a credit card" do
let(:card_number) { "123456789012" }
it "accepts cards" do
pending
# do something with card_number
end
context "failure" do
let(:card_number) { "BLUEBEARD666" }
it "doesn't handle weird formats" do
# oh yeah!
end
end
end
In this case you have your embedded let
s but it makes more sense semantically.
I hope this doesn't miss the point entirely :P
Upvotes: 0