In order to avoid repeating myself a lot in my Rspec tests, I'd like to write a function like this
def with_each_partner(&block)
PARTNER_LIST.each do |partner|
context "with partner #{partner.name}" { yield partner }
end
end
I have such a function and it works in the sense that all the tests run with the correct value for partner supplied, but they don't print during output as being part of the "with partner X" context: instead if I have a test like this:
describe Thing do
subject { Thing.new(partner) }
with_each_partner do |partner|
it 'does its thing' do
expect(subject.do).to eq 'its thing'
end
end
end
I end up with output like this:
Thing
does its thing
Instead of the desired output, which is like:
Thing
with partner X
does its thing
with partner Y
does its thing
How can I get RSpec to correctly work with the context created in my function?
TL;DR: do this:
def with_each_partner(&block)
PARTNER_LIST.each do |partner|
context "with partner #{partner.name}" do
class_exec(&block)
end
end
end
RSpec's DSL works by evaluating the blocks with a changed self
-- this is how it
is a method within a describe
or context
block, but not outside of it. When you yield
, the provided block is evaluated with the original self
that was self
at the point the block was defined. What that means is that with your original with_each_partner
definition, this code:
describe Thing do
subject { Thing.new(partner) }
with_each_partner do |partner|
it 'does its thing' do
expect(subject.do).to eq 'its thing'
end
end
end
Is really being evaluated like this:
describe Thing do
subject { Thing.new(partner) }
outer_example_group = self
with_each_partner do |partner|
outer_example_group.it 'does its thing' do
expect(subject.do).to eq 'its thing'
end
end
end
...and so the individual examples are defined on the outer example group, not on the "with partner #{partner.name}"
nested group.
class_exec
evaluates the provided block in the context of the class/module. In this case, the class is the example group subclass that RSpec has generated for your context. Using class_exec
ensures that when it
is called, the receiver is your nested context
example group rather than the outer example group, creating the result you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With