2015-05-16 125 views
0

设计用户注册和登录。水豚和管理员用户权限(和设计)的问题

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

Rspec和FactoryGirl。

不幸的是,我写我的应用程序编写测试之前(最好的教训学到了艰辛的道路)。我现在正在学习rspec并为应用程序编写测试套件。

我有控制器的权限和查看权限设置,管理员和非管理员,这在实践中做的工作(我知道这通过彻底的浏览器手动测试)。

在这种情况下,我在当用户登录其上显示,也是一个管理员的报头中的“系统管理员”链接。

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

我写了一些这方面的测试,以为我拥有排序,直到我注意到,更改到包含这些测试的具体/功能规格文件时,卫兵只运行这些测试,并将它们传递。但是,当我运行整个测试套件时,那些相同的测试失败。我完全被这个困惑了。

我认为这可能与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 => '[email protected]') 
    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 ==) 

其中,对我来说,意味着管理页面访问,当它应该不会迟到了。在我的浏览器中,无法看到管理页面链接,也不能通过手动输入网址访问页面,除非用户已登录并且是管理员。

的背景下,“如果用户是管理员”运行完整的测试套件时,全部通过测试。

规格/工厂/ 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部分:

<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' 
+0

在你的天赋 - 尝试检查的东西,用户实际会/不会在管理页面上看到,例如'expect(page).not_to have_content(“Welcome Mr Admin”)'或者登录管理页面上实际存在的任何内容 - 如果你能看到,那么是的,你需要弄清楚为什么人们会自动登录到管理员。否则,它可能是他们在管理员登录页面? –

+0

此外,样式点:使用'&&'和'||'而不是'和'和'或'(阅读为什么在这里:http://devblog.avdi.org/2010/08/02/using-and-and第二:'current_user.admin == true'这是非常危险的......如果你不小心忘记了其中一个'='会发生什么 - 这是一个很难发现的错误,并且不会真正导致你的错误代码失败...只是默默地让所有用户看到管理页面。总是这样做是一个好习惯:'true == current_user.admin'如果不小心使用了'=',那么这会失败 –

回答

0

你的测试失败的原因是您在重定向之前检查current_path 已完成。基本上你打电话访问(xxx),它将current_path设置为xxx,然后立即读回xxx,而服务器将重定向返回到/,然后浏览器将current_path更改为/。只要你使用的水豚2.5+你应该使用have_current_path匹配,这将重试了一下,从而使重定向实时处理

expect(page).to have_current_path(root_path)