How to parameterize an RSpec shared context
I had a RSpec shared_context which was creating a FactoryGirl user mock and then logging in with it, and then running some shared examples for testing permissions on a generic user. I needed to modify it to accept a parameter of using a different user.
It originally looked like this:
RSpec.shared_context "auth" do
let(:current_user) { FactoryGirl.create(:user) }
before(:each) do
allow(User).to receive(:find_by_id)
allow(User).to receive(:find_by_id).with(current_user.id) { current_user }
end
shared_context "logged in" do
before(:each) do
login_as current_user
end
end
end
In my spec files, it was included by doing this:
describe MyController do
include_context 'auth'
context "when logged in as a normal user" do
include_context 'logged in'
...
end
end
However I needed MyController to override current_user and use an admin user instead. Rather conveniently, you can override a context by sending it a block.
describe MyController do
include_context 'auth'
context "when logged in as an admin" do
include_context 'logged in' do
let(:current_user) { FactoryGirl.create(:admin) }
end
...
end
end
This works because the let inside of the block given is called again, and the new block calls let(:current_user)
again and overrides the original block and FactoryGirl.create(:user)
is never called.
This is documented here, however they don't really show that it can be used for making your shared_context parameterized. So I'm going to say the word parameterized a lot here, and hope that when I (or you!) google "How do I parameterize an RSpec shared_context?", you find this post.