RSpec controller specs fail after adding authoriza

2020-03-30 06:43发布

问题:

Following Railscast #385 & #386 I have added authorization to my Rails 4 application (rails 4.2.6, rspec-rails 3.4.2). After adding authorization, all of my controller specs fail.

My feature specs still pass because in the spec I login as admin to perform actions that are not allowed visitors (:edit, :update...). If I make all actions allowed to a visitor my controller specs pass. However, any action that only the admin can perform will fail in the controller spec.

The scaffold generator creates an empty

  let(:valid_session) { { } }

that it says in the comments:

# This should return the minimal set of values that should be in the session in order to pass any filters (e.g. authentication) defined in MyController.

However, I don't know how to configure this :valid_session to allow authentication to pass.

When I try the solution posted in this question:

def valid_session
  controller.stub!(:signed_in?).and_return(true)
end

I get this error:

NoMethodError:
        undefined method `stub' for #<MyController

Since, all of the many branching paths of logic should be tested at a lower level (ie the controller spec) rather than the feature spec, how do I get the controller specs to pass?

How do I configure valid_session or what other method can I use to pass authorization in my controller specs?

回答1:

Ok, with the help of this blog I was able to get my controller tests to pass after adding authorization to the controllers.

In spec/support/helpers/controller_macros.rb I defined these methods:

module ControllerMacros
  def login_admin
    user = FactoryGirl.create(:admin)
    allow(controller).to receive(:current_user).and_return(user)
  end
  def login_member
    user = FactoryGirl.create(:member)
    allow(controller).to receive(:current_user).and_return(user)
  end
  def login_visitor
    allow(controller).to receive(:current_user).and_return(nil)
  end
end

To activate those methods in spec/support/helpers.rb:

RSpec.configure do |config|
  config.include ControllerMacros, type: :controller
end

And then implement it in the controller spec, spec/controllers/topics_controller_spec.rb:

require 'rails_helper'
RSpec.describe TopicsController, type: :controller do
  let(:valid_attributes) {
    { :name => "MyTopic" }
  }
  let(:invalid_attributes) {
    { :name => nil }
  }
  before(:each) do
    login_admin
  end
  describe "GET #index" do
    it "assigns all topics as @topics" do
      login_visitor
      topic = Topic.create! valid_attributes
      get :index, {}
      expect(assigns(:topics)).to eq([topic])
    end
  end
  describe "GET #show" do
    it "assigns the requested topic as @topic" do
      login_visitor
      topic = Topic.create! valid_attributes
      get :show, {:id => topic.to_param}
      expect(assigns(:topic)).to eq(topic)
    end
  end
  describe "GET #new" do
    it "assigns a new topic as @topic" do
      get :new, {}
      expect(assigns(:topic)).to be_a_new(Topic)
    end
  end
[...]
end

This will login as admin before each test. Note that you should test each action at the lowest level that has permission, so show and index are tested as a visitor (not-logged in). If all actions need admin permission then you can use before do instead of before(:each) do to save a little time in the tests.

Finally you must remove all mention of valid_session from the spec.



回答2:

You could just factory a user.

module ControllerMacros
  def login_user(user = nil, options = {})
    before(:each) do
    @request.env["devise.mapping"] = Devise.mappings[:user]
    @user = user || FactoryGirl.create(options[:user] || :user)
    sign_in @user
  end
end