Reputation: 4073
I want to share a memoized method between my specs. So I tried to use shared context like this
RSpec.configure do |spec|
spec.shared_context :specs do
let(:response) { request.execute! }
end
end
describe 'something' do
include_context :specs
end
It works ok. But I have about 60 spec files, so I'm forced to explicitly include context in each of them. Is there an way to automatically include shared context (or at least let
definition) for all example groups in spec_helper.rb
?
Something like this
RSpec.configure do |spec|
spec.include_context :specs
end
Upvotes: 17
Views: 6500
Reputation: 10856
In RSpec 3+, this can be achieved as follows - based on Jeremy Peterson's answer.
# spec/supprt/users.rb
module SpecUsers
extend RSpec::SharedContext
let(:admin_user) do
create(:user, email: '[email protected]')
end
end
RSpec.configure do |config|
config.include SpecUsers
end
Upvotes: 6
Reputation: 13037
Another way to go is to automatically share examples via metadata. So:
shared_context 'a shared context', a: :b do
let(:foo) { 'bar' }
end
describe 'an example group', a: :b do
# I have access to 'foo' variable
end
The most common way I use it is in rspec-rails, with some shared context depending on the example group type. So if you have config.infer_spec_type_from_file_location!
, you can simply do:
shared_context 'a shared context', type: :controller do
let(:foo) { 'bar' }
end
describe SomeController do
# I have access to 'foo' variable
end
Upvotes: 3
Reputation: 3784
Also if you need ability to use shared data in before
blocks inside specs, as me, try to include this (if its Rails project):
module SettingsHelper
extend ActiveSupport::Concern
included do
attr_reader :default_headers
before :all do
@default_headers = Hash[
'HTTP_HOST' => 'test.lvh.me'
]
end
after :all do
@default_headers = nil
end
end
end
RSpec.configure do |config|
config.include SettingsHelper
end
Or try something similar, look at @threedaymonk answer.
Upvotes: 0
Reputation: 525
You can do it almost like that: there's a mechanism for including a module, and module inclusion has its own callback mechanism.
Suppose for example that we have a disconnected
shared context that we want to use to run all our model specs without a database connection.
shared_context "disconnected" do
before :all do
ActiveRecord::Base.establish_connection(adapter: :nulldb)
end
after :all do
ActiveRecord::Base.establish_connection(:test)
end
end
You can now create a module that will include that context on inclusion.
module Disconnected
def self.included(scope)
scope.include_context "disconnected"
end
end
Finally, you can include that module into all specs in the normal manner (I've demonstrated doing it only for models, just to show that you can), which is almost exactly what you asked for.
RSpec.configure do |config|
config.include Disconnected, type: :model
end
That works with rspec-core
2.13.0 and rspec-rails
2.13.0.
Upvotes: 5
Reputation: 666
You can set up global before
hooks using RSpec.configure
via configure-class-methods and Configuration:
RSpec.configure {|c| c.before(:all) { do_stuff }}
let
is not supported in RSpec.configure
, but you can set up a global let
by including it in a SharedContext module and including that module using config.before
:
module MyLetDeclarations
extend RSpec::Core::SharedContext
let(:foo) { Foo.new }
end
RSpec.configure { |c| c.include MyLetDeclarations }
Upvotes: 33