2

I'm having trouble using a defined variable for a shared example in RSpec. Here's my test:

RSpec.shared_examples "check user logged in" do |method, action, params|
  it "redirects to the sign in page if the user is not logged in" do
    send(method, action, params)
    expect(response).to redirect_to(signin_url)
  end
end

RSpec.describe UsersController, type: :controller do
  describe "GET #show" do
    let(:user) { FactoryGirl.create(:user) } 
    let!(:show_params) do 
      return { id: user.id }
    end

    context "navigation" do
      include_examples "check user logged in", :get, :show, show_params
    end
  end
end

In the test, I'm checking to make sure the user needs to be logged in before the action can be performed. I am receiving the following error message:

method_missing': show_params is not available on an example group

What do I need to change to make show_params accessible? I've tried using it_behaves_like instead of include_examples with no luck. I've also tried removing the context "navigation" block to no avail. I need to perform this check across multiple controllers and actions, so it seems a shared example might be the correct way to reuse code.

1 Answer 1

3

The problem here is that the memoized let helper show_params is called outside of an example.

Instead of passing the params you can simply reference a let from the outer scope where you are including the example:

RSpec.describe UsersController, type: :controller do
  let(:user) { FactoryGirl.create(:user) }
  describe "GET #show" do
    let(:action) { get :show, id: user }
    it_should_behave_like "an authorized action"
  end
end

RSpec.shared_examples "an authorized action" do
  it "denies access" do
    action
    expect(response).to redirect_to(signin_url) 
  end
end

This is a pretty powerful pattern that lets you use a convention over configuration approach since the last let always wins.

RSpec.describe UsersController, type: :controller do
  let(:user) { FactoryGirl.create(:user) }
  describe "GET #show" do
    let(:action) { get :show, id: user }
    it_should_behave_like "an authorized action"

    context "when signed in" do
      before { sign_in user }
      let(:action) { get :show, id: other_user }
      context 'when viewing another user' do
        it_should_behave_like "an authorized action"
      end
    end
  end
end
Sign up to request clarification or add additional context in comments.

1 Comment

This worked beautifully! Thank you very much! I put the shared example in a separate file (spec/controllers/shared_examples/authorized_action.rb), required that directory in my spec/rails_helper.rb, and then used it as you suggested!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.