Reputation: 810
Is it possible to modify an existing let
variable, such as adding an entry to a hash:
describe 'my spec' do
let(:var) { { a: 1, b: 2 } }
let(:foo) { bar(var) }
context 'when c is defined' do
let(:var) { var.merge c: 3 } # This does not work because it will be evaluated recursively
# ... actual tests using foo ...
end
end
I want var
to be { a: 1, b: 2, c: 3 }
.
Upvotes: 1
Views: 3041
Reputation: 3152
If you have nested RSpec contexts to test the behaviour of different parameters, you can partially override the parent parameters by merging the changed parameters into super()
in your let(:foo)
block:
describe 'My::Class' do
let(:foo) do
{
'some_common_param' => 'value'
'role' => 'default',
}
end
context 'with role => web' do
let(:foo) do
super().merge({ 'role' => 'web' })
end
it { should compile }
end
end
Upvotes: 3
Reputation: 16793
Just re-define var
in each context
in the form it is needed in. foo
is evaluated lazily so it will use the appropriate version of var
defined in each context
when it is eventually called in the it
block:
describe 'my spec' do
let(:foo) { bar(var) }
context 'when c is defined' do
let(:var) { { a: 1, b: 2, c: 3 } }
it 'returns some result with c' do
expect(foo).to eq('bar with c') # or whatever it returns
end
end
context 'when d is defined' do
let(:var) { { a: 1, b: 2, d: 4 } }
it 'returns some result with d' do
expect(foo).to eq('bar with d') # or whatever it returns
end
end
end
Edit: if you really want nested let
s, then I'd say either go with Igor's answer, or if the base definition of var
won't be tested in the specs, then put it in a separate let
statement (if it is tested, then you'll get unavoidable repetition as per the final example):
describe 'my spec' do
let(:base_var) { { a: 1, b: 2 } }
let(:foo) { bar(var) }
context 'when c is defined in var' do
let(:var) { base_var.merge(c: 3) }
it 'returns some result with c' do
expect(foo).to eq('bar with c') # or whatever it returns
end
end
context 'when d is defined in var' do
let(:var) { base_var.merge(d: 4) }
it 'returns some result with d' do
expect(foo).to eq('bar with d') # or whatever it returns
end
end
context 'when no changes made from base_var to var' do
let(:var) { base_var }
it 'returns some result from just bar' do
expect(foo).to eq('just bar') # or whatever it returns
end
end
end
Upvotes: 0
Reputation: 738
Yes, you created a circular dependency, it won't work.
I think the best solution is to set the var
content static on contexts that it needs to be changed:
...
let(:var) { { a: 1, b: 2, c: 3 } }
...
If you really need to merge the hash with something else, the following workaround will do the trick, changing the existing hash in place with the before
callback:
describe 'my spec' do
let(:var) { { a: 1, b: 2 } }
let(:foo) { bar(var) }
context 'when c is defined' do
before { var.merge! c: 3 }
end
end
Upvotes: 1