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.

Post a Comment

Your email is kept private. Required fields are marked *