Search code examples
ruby-on-railsruby-on-rails-4rspecrspec-railsrspec3

How do I test the comments of the current_user in RSpec?


So I have a User that has_many :comments.

In my Comments#Index, I have this:

  def index
    @comments = current_user.comments
  end

Inside my Rspec.config... block in rails_helper.rb I have this:

  # Add Config info for Devise
  config.include Devise::TestHelpers, type: :controller

My comments_controller_spec.rb looks like this:

  describe 'GET #index' do
    it "populates an array of comments that belong to a user" do
      user = create(:user)
      node = create(:node)
      comment1 = create(:comment, node: node, user: user)
      comment2 = create(:comment, node: node, user: user)
      get :index, { node_id: node  }
      expect(assigns(:comments)).to match_array([comment1, comment2])
    end
    it "renders the :index template"
  end

This is my Users.rb factory:

FactoryGirl.define do
  factory :user do
    association :family_tree
    first_name { Faker::Name.first_name }
    last_name { Faker::Name.last_name }
    email { Faker::Internet.email }
    password "password123"
    password_confirmation "password123"
    bio { Faker::Lorem.paragraph }
    invitation_relation { Faker::Lorem.word }
    # required if the Devise Confirmable module is used
    confirmed_at Time.now
    gender 1
  end
end

This is my Comments factory:

FactoryGirl.define do
  factory :comment do
    association :node
    message { Faker::Lorem.sentence }

    factory :invalid_comment do
      message nil
    end
  end
end

This is the error I am getting now:

 Failure/Error: get :index, { node_id: node  }
 NoMethodError:
   undefined method `comments' for nil:NilClass

Thoughts?


Solution

  • You need to sign in first:

    describe 'GET #index' do
      let(:user) { create(:user) }
      let(:node) { create(:node) }
      let(:comment1) { create(:comment, node: node, user: user) }
      let(:comment2) { create(:comment, node: node, user: user) }
    
      before do
        @request.env["devise.mapping"] = Devise.mappings[:user]
        sign_in user
      end
    
      it "populates an array of comments that belong to a user" do
        get :index, { node_id: node }
        expect(assigns(:comments)).to match_array [comment1, comment2]
      end
    end
    

    You could also create a module in your spec/support directory with the following code:

    module SpecAuthentication
      def login_user
        @request.env["devise.mapping"] = Devise.mappings[:user]
        @user = FactoryGirl.create :user
        sign_in @user
      end
    end
    

    and include it in your RSpec.configure block:

    config.include SpecAuthentication
    

    Now you can call the login_user method in your specs:

    describe 'GET #index' do
      let(:node) { create(:node) }
      let(:comment1) { create(:comment, node: node, user: @user) }
      let(:comment2) { create(:comment, node: node, user: @user) }
    
      before { login_user }
    
      it "populates an array of comments that belong to a user" do
        get :index, { node_id: node }
        expect(assigns(:comments)).to match_array [comment1, comment2]
      end
    end
    

    Update

    Instead of including the module in the configure block in your spec/rails_helper.rb file, you could also add a configure block in the support file (spec/support/devise.rb) itself:

    module SpecAuthorization
      ...
    end
    
    RSpec.configure do |config|
      config.include SpecAuthorization
    end