Capybara和管理员用户权限(和设计)的问题

时间:2015-05-16 14:50:11

标签: ruby-on-rails-4 rspec capybara factory-bot

设计用户注册和登录。

使用rails控制台设置管理员(将admin boolean设置为true)。

Rspec和FactoryGirl。

不幸的是,我在编写测试之前编写了我的应用程序(最好的课程是通过艰难的方式学习的)。我现在正在学习rspec并为应用程序编写测试套件。

我为管理员和非管理员设置了控制器权限和查看权限,这些权限在实践中有效(我通过全面的浏览器手动测试了解到这一点)。

在这种情况下,我在标题中有一个“管理员”链接,当用户登录并且也是管理员时会显示该链接。

我的StaticPagesController也有一个有效的before_action设置,所以没有人可以访问管理页面,除非他们已登录并且也是管理员。

我为此编写了一些测试,并且认为我对它进行了排序,直到我注意到,当对包含这些测试的特定/功能规范文件进行更改时,guard仅运行那些测试并传递它们。但是,当我运行整个测试套件时,那些相同的测试失败了。我对这完全感到困惑。

我认为它可能与Devise有关,但我只是不知道。

规格/特征/ user_and_role_spec.rb

require 'rails_helper'

def manually_create_user
    visit new_user_registration_path
    fill_in('user_first_name', :with => 'Test')
    fill_in('user_last_name', :with => 'User')
    fill_in('user_email', :with => 'testuser@email.com')
    fill_in('user_password', :with => 'testuser')
    click_button('Sign up')
end

def create_user_and_login_as(type)
    user = FactoryGirl.create(type)
    visit(new_user_session_path)
    fill_in('user_email', :with => user.email)
    fill_in('user_password', :with => user.password)
    click_button('Log in')
end


describe 'with users and roles' do  

    context "if user is not an admin" do

        it "does not allow any user to visit the admin page if not logged-in" do
            visit(admin_path)
            expect(current_path).to eq(root_path)
        end

        it "does not allow a new user to visit the admin page" do 
            manually_create_user
            visit(admin_path)
            expect(current_path).to eq(root_path)
        end

        it "does not allow a student to visit the admin page" do 
            create_user_and_login_as(:student)
            visit admin_path
            expect(current_path).to eq(root_path)
        end

        it "does not allow a teacher to visit the admin page" do 
            create_user_and_login_as(:teacher)
            visit admin_path
            expect(current_path).to eq(root_path)
        end

    end


    context "if user is an admin" do

        it "allows an admin user to visit the admin page" do 
            create_user_and_login_as(:admin_user)
            click_link 'Admin'
            expect(current_path).to eq(admin_path)
        end

        it "allows a teacher_admin to visit the admin page" do 
            create_user_and_login_as(:teacher_admin_user)
            click_link 'Admin'
            expect(current_path).to eq(admin_path)
        end

    end

end

在运行完整测试套件时,“如果用户不是管理员”上下文中的测试全部失败。他们都失败了同样的错误:

Failure/Error: expect(current_path).to eq(root_path)

       expected: "/"
            got: "/admin"

       (compared using ==)

对我而言,这意味着管理页面可以访问,而不应该访问。在我的浏览器中,无法看到管理页面链接,也无法通过手动输入网址来访问该页面,除非用户已登录并且是管理员。

在运行完整测试套件时,“如果用户是管理员”上下文中的测试都是PASS。

规格/工厂/ users.rb的:

require 'faker'

FactoryGirl.define do
    factory :user do |f|
        f.first_name    { Faker::Name.first_name }
        f.last_name     { Faker::Name.last_name }
        f.email         { Faker::Internet.email }
        f.password      { Faker::Internet.password(8) }
        f.admin         false

        trait :student do
            type "Student"
        end

        trait :teacher do 
            type "Teacher"
        end

        trait :admin do 
            admin true
        end

        factory :admin_user,            traits: [:admin]
        factory :student,               traits: [:student]
        factory :teacher,               traits: [:teacher]
        factory :teacher_admin_user,    traits: [:teacher, :admin]

    end
end

static_pages_controller.rb:

class StaticPagesController < ApplicationController
    before_action :admin?, only: [:admin]

  def home
    @testimonials = Testimonial.all
  end

  def admin
    @groups = Group.all

    @users = User.all

    @students = Student.all

    @teachers = Teacher.all
  end

  private

  def admin?
    unless signed_in? and current_user.admin == true
        redirect_to root_path, notice: "You must be a signed-in admin to view this page"
    end
  end

end

static_pages_helper.rb:

module StaticPagesHelper

    def allowed_to_see_admin_link?
        signed_in? && current_user.admin
    end

end

模型/ user.rb:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :admin, inclusion: { in: [true, false] }

  scope :newest_first, -> { order("created_at DESC") }
  scope :order_by_first_name, -> { order("first_name") }

  def full_name
    "#{first_name} #{last_name}"
  end

  def unassigned?
    type != "Student" and type != "Teacher"
  end

  def can_view_materials?
    admin || type == "Teacher" || type == "Student" && groups.any? # So only current students can view the Materials page.
  end

  def testimonial_owner?(testimonial)
    id == testimonial.student_id
  end

end

_header.html.erb partial的相关部分:

<ul class="nav navbar-nav navbar-right">
        <% if allowed_to_see_admin_link? %>
          <li><%= link_to "Admin", admin_path %></li>
        <% end %>
      </ul>

的Gemfile:

gem 'rails', '4.2.0'
gem 'bootstrap-sass', '~> 3.3.3'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'devise'

group :development, :test do
    gem 'sqlite3'
    gem 'byebug'
    gem 'web-console', '~> 2.0'
    gem 'spring'
    gem 'better_errors'
    gem 'binding_of_caller'
    gem 'rspec-rails'
    gem 'guard-rspec', require: false
    gem 'factory_girl_rails'
end

group :test do
    gem 'faker'
    gem 'capybara'
    gem 'launchy'
    gem 'database_cleaner'
end

group :production do
    gem 'pg'
    gem 'rails_12factor'
end
模式中的

用户:

create_table "users", force: :cascade do |t|
    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.datetime "created_at"
    t.datetime "updated_at"
    t.boolean  "admin",                  default: false
    t.string   "type"
    t.string   "first_name"
    t.string   "last_name"
  end

routes.rb中:

resources :materials

  root 'static_pages#home'

  devise_for :users, :controllers => { registrations: 'registrations' }

  get 'admin' => 'static_pages#admin'

  resources :groups
  resources :users
  resources :students
  resources :teachers
  resources :testimonials
  post 'assign_to_group' => 'students#assign_to_group' # Could have been 'patch', but default in the controller method is 'post', so I left the method as default and changed this route to 'post'. Doesn't NEED to be patch.
  post 'remove_from_group' => 'students#remove_from_group'
  post 'unassign_teacher' => 'groups#unassign_teacher'
  post 'assign_as_student' => 'teachers#assign_as_student'
  post 'assign_as_teacher' => 'students#assign_as_teacher'
  post 'add_student' => 'groups#add_student'
  post 'remove_student_from_group' => 'groups#remove_student_from_group'

1 个答案:

答案 0 :(得分:0)

您的测试失败的原因是您在重定向完成之前检查current_path。基本上你正在调用visit(xxx),它将current_path设置为xxx,然后立即读回xxx,同时服务器将重定向返回给/然后浏览器将current_path更改为/。只要您使用Capybara 2.5+,就应该使用has_current_path匹配器,它将重试一段时间,从而为重定向时间提供处理

expect(page).to have_current_path(root_path)