I'm working through a Rails course trying to function test my app. I'm running into an issue that seems to indicate that Rspec isn't using the helper needed for it to work with Devise (CanCanCan).
Link to repo below. I'm a fairly new developer but make an effort for it to be readable. Thanks, all feedback appreciated.
The issue comes from my users_controller_spec.rb file:
Failures:
1) UsersController GET #show User is logged in loads correct user details
Failure/Error: if user&.admin?
NoMethodError:
undefined method `admin?' for #<User:0x00000005c08eb0>
# ./app/models/ability.rb:14:in `initialize'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:361:in `new'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:361:in `current_ability'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/cancancan-1.15.0/lib/cancan/controller_additions.rb:342:in `authorize!'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:49:in `authorize_resource'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:34:in `load_and_authorize_resource'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/cancancan-1.15.0/lib/cancan/controller_resource.rb:10:in `block in add_before_action'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/rails-controller-testing-1.0.1/lib/rails/controller/testing/template_assertions.rb:61:in `process'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:33:in `block in process'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:100:in `catch'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:100:in `_catch_warden'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/devise-4.2.0/lib/devise/test/controller_helpers.rb:33:in `process'
# /home/scorpian55/.rvm/gems/ruby-2.3.1@unit4-1/gems/rails-controller-testing-1.0.1/lib/rails/controller/testing/integration.rb:12:in `block (2 levels) in <module:Integration>'
# ./spec/controllers/users_controller_spec.rb:15:in `block (4 levels) in <top (required)>'
Finished in 0.36791 seconds (files took 4.43 seconds to load)
7 examples, 1 failure
Failed examples:
rspec ./spec/controllers/users_controller_spec.rb:14 # UsersController GET #show User is logged in loads correct user details
In researching, most errors I've seen have been around the NoMethodError for nil:NilClass and I am not getting that same error, that is a different issue and not the exact same error message.
I've checked:
1) spec/rails_helper.rb has:
config.include Devise::Test::ControllerHelpers, :type => :controller
2) spec/spec_helper.rb has:
require 'spec_helper'
require 'rspec/rails'
# note: require 'devise' after require 'rspec/rails'
require 'devise'
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, :type => :controller
end
Here are the relevant files (and my repo: https://github.com/ScorpIan555/gitwork):
users_controller_spec.rb
require 'rails_helper'
describe UsersController, :type => :controller do
let(:user) { User.create!(email: "user#{rand(100000).to_s}@examples.com", password: '1234567890') }
describe 'GET #show' do
context 'User is logged in' do
before do
sign_in user
end
it 'loads correct user details' do
get :show, id: user.id
expect(response).to have_http_status(200)
expect(assigns(:user)).to eq user
end
end #end of first context
context 'No user is logged in' do
it 'redirects to login' do
get :show, id: user.id
expect(response).to redirect_to(new_user_session_path)
end
end
end #end of GET#show block
end #end of whole block
ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
alias_action :create, :read, :update, :destroy, to: :crud
can :manage, User, id: user.id
if user&.admin?
can :crud, Product
can :crud, User
can :crud, Comment
elsif user&.signed_in?
can :read, Comment
can :create, Comment
can :read, Product
#can :invite, :User
else
can :read, Comment
can :read, Product
end #end if/else
end #end def initialize(user)
end #end class Ability
users_controller.rb:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
load_and_authorize_resource
# GET /users
# GET /users.json
def index
@users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
@user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
@user = User.new(user_params)
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: @user }
else
format.html { render :new }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:first_name, :last_name)
end
end
schema.db
ActiveRecord::Schema.define(version: 20161219012011) do
create_table "comments", force: :cascade do |t|
t.integer "user_id"
t.text "body"
t.integer "rating"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_comments_on_product_id"
t.index ["user_id"], name: "index_comments_on_user_id"
end
create_table "orders", force: :cascade do |t|
t.integer "user_id"
t.integer "product_id"
t.float "total"
t.index ["product_id"], name: "index_orders_on_product_id"
t.index ["user_id"], name: "index_orders_on_user_id"
end
create_table "products", force: :cascade do |t|
t.string "name"
t.text "description"
t.string "image_url"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "color"
t.decimal "price"
end
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.boolean "admin", default: false, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :orders
has_many :comments #added troubleshooting 6.3, need to test
end
load_and_authorize_resource
? – 31piyUser
model? – 31piyUser
is not connected with each other. That's why the ability class is not able to call user'sadmin?
method. – 31piy