Search code examples
ruby-on-railsrubyrspec-railsruby-on-rails-6activesupport-concern

Testing Controller Concerns with Rspec on Rails 6


I am not able to access session or route paths (root_url) from the specs using anonymous controller on a controller concern.

Here is my code

module SecuredConcern
  extend ActiveSupport::Concern

  def logged_in?
    session[:user_info].present?
  end

  def redirect_to_login
    redirect_to login_path unless logged_in?
  end

  def redirect_to_home
    redirect_to root_path if logged_in?
  end
end

And spec

require 'rails_helper'

describe SecuredConcern, type: :controller do
  before do
    class FakesController < ApplicationController
      include SecuredConcern
    end
  end

  after { Object.send :remove_const, :FakesController }
  let(:object) { FakesController.new }
  # let(:session) {create(:session, user_info: '')}

  describe 'logged_in' do
    it "should return false if user is not logged in" do
      expect(object.logged_in?).to eq(false)
    end
  end
end

Here is the trace:

Module::DelegationError:
       ActionController::Metal#session delegated to @_request.session, but @_request is nil: #<FakesController:0x00007f9856c04c20 @_action_has_layout=true, @rendered_format=nil, @_routes=nil, @_request=nil, @_response=nil>
     # ./app/controllers/concerns/secured_concern.rb:9:in `logged_in?'
     # ./spec/controllers/concerns/secured_concern_spec.rb:21:in `block (3 levels) in <main>'
     # ------------------
     # --- Caused by: ---
     # NoMethodError:
     #   undefined method `session' for nil:NilClass
     #   ./app/controllers/concerns/secured_concern.rb:9:in `logged_in?'

config is updated with config.infer_base_class_for_anonymous_controllers = true

Any pointers on what I am doing wrong here?


Solution

  • Here is how I solved it by using RSpec.shared_examples

    In my controller spec where I am including this concern:

    # spec/controllers/home_controller_spec.rb
    RSpec.describe HomeController, type: :controller do
      it_behaves_like 'SecuredConcern', HomeController
    end
    

    And in my concern spec:

    # spec/shared/secured_concern_spec.rb
    require 'rails_helper'
    
    RSpec.shared_examples 'SecuredConcern' do |klass|
      describe '#logged_in?' do
        it "should return true if user is logged in" do
          session[:user_info] = {uid: 1}
          expect(subject.logged_in?).to eq(true)
        end
    
        it "should return false if user is not logged in" do
          expect(subject.logged_in?).to eq(false)
        end
      end
    end
    

    Hope it helps anyone with a similar issue.