Controller tests - thuy-econsys/rails_app GitHub Wiki

Define FactoryBot User

Use FactoryBot's trait to setup different users.

FactoryBot.define do
  factory :user, class: User do
    sequence(:email) { |n| "user-#{n}@example.com" }
    password {"Password1"}
    password_confirmation {"#{password}"}
    account_type {:author}

    trait :admin_user do
      account_type {:admin}
    end

    trait :moderator_user do
      account_type {:moderator}
    end

    factory :admin_user, traits: [:admin_user] 
    factory :moderator_user, traits: [:moderator_user] 
  end
end

Define helper methods

# /spec/support/controller_macros.rb

module ControllerMacros

  def login_user
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user]
      sign_in create(:user)
    end
  end

  def login_admin
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:admin_user]
      sign_in create(:admin_user)
    end
  end

  def login_moderator
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:moderator_user]
      sign_in create(:moderator_user)
    end
  end
end

Include and require files

# /spec/rails_helper.rb
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
# ...

require 'rspec/rails'
# ...

require 'support/controller_macros'

RSpec.configure do |config| 
  # ...

  config.include Devise::Test::ControllerHelpers, :type => :controller
  config.include FactoryBot::Syntax::Methods
  config.extend ControllerMacros, :type => :controller
end

Use the ControllerMacros methods to test code

# /spec/controllers/articles_controller_spec.rb
require 'rails_helper'

RSpec.describe ArticlesController, type: :controller do 

  let(:valid_attributes) {
    {  
      :title => "Title",
      :content => "Lorem ipsum..."
    }
  }

  describe "GET index" do
    context "when user logged in" do
      login_user # ControllerMacros module method

      it "can view all articles" do
        get :index
        expect(response).to be_successful
      end
      it "can not delete articles" do
        Article.create!(valid_attributes)
        expect {
          delete :destroy, params: {
            :id => @article.to_param
          }
        }.to change{ Article.count }.by(0)        
      end 
    end

    context "when admin logged in" do
      login_admin # ControllerMacros module method

      it "can view all articles" do
        get :index
        expect(response).to be_successful
      end
      it "can delete articles" do
        Article.create!(valid_attributes)
        expect {
          delete :destroy, params: {
            :id => @article.to_param
          }
        }.to change{ Article.count }.by(-1)        
      end 
    end
  end
end

Use Request instead of Controller Specs

RSpec and Rails are both encouraging specs be written for Request and not Controller. There is currently still support for Controller specs, but guessing that there will be more deprecation of Controller spec functionality down the road. Based on the release of RSpec 3.5:

The official recommendation of the Rails team and the RSpec core team is to write request specs instead.

References