I have this code that I want to reuse in several specs:
RSpec.shared_context "a UserWorker" do |user|
let(:mock_context_user) {{
id: 1,
brand: user.brand,
backend_token: user.backend_token
}}
before(:each) do
allow(SomeClass).to receive(:some_method)
.with(user.id).and_return(mock_context_user)
end
before(:each, context: true) do
Sidekiq::Testing.inline!
end
after(:each, context: true) do
Sidekiq::Testing.fake!
end
end
And in the spec file that uses the shared code:
let(:user) { build :user } # FactoryGirl
...
describe '#perform' do
# some lets here
include_context 'a UserWorker', user
context 'when something exists' do
it 'does some stuff' do
# test some stuff here
end
end
end
But that gives me this error:
/.rvm/gems/ruby-2.3.0@fb-cont/gems/rspec-core-3.5.1/lib/rspec/core/example_group.rb:724:in `method_missing': `user` is not available on an example group (e.g. a `describe` or `context` block). It is only available from within individual examples (e.g. `it` blocks) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). (RSpec::Core::ExampleGroup::WrongScopeError)
Suggestions? Any help is appreciated.
The RSpec docs aren't very clear on this, but you can inject additional values by passing a block containing let()
calls to include_context
. The "customization block" passed by the spec will be evaluated first, and is available to the code declared in the shared context.
Here's a shared context that depends on the specs including it to let()
a value, value_from_spec
, and then sets a couple more values, one via let()
and one via a before()
block:
RSpec.shared_context('a context', shared_context: :metadata) do
# assume the existence of value_from_spec
let(:a_value_from_context) { value_from_spec - 1 }
before(:each) do
# assume the existence of value_from_spec
@another_value_from_context = value_from_spec + 1
end
end
(Note that unlike the OP's |user|
example, we never explicitly declare value_from_spec
, we just trust that it'll be there when we need it. If you want to make what's going on more obvious, you could check defined?(:value_from_spec)
and raise an error.)
And here's a spec that injects that value, and reads the shared context's transformations of it:
describe 'passing values to shared context with let()' do
# "customization block"
include_context 'a context' do
# set value_from_spec here
let(:value_from_spec) { 1 }
end
describe 'the context' do
it 'should read the passed value in a let() block' do
expect(a_value_from_context).to eq(0)
end
it 'should read the passed value in a before() block' do
expect(@another_value_from_context).to eq(2)
end
end
end
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