【发布时间】:2026-01-15 22:15:01
【问题描述】:
设计用于用户注册和登录。
使用 Rails 控制台设置管理员(将 admin 布尔值设置为 true)。
Rspec 和 FactoryGirl。
不幸的是,我在编写测试之前编写了我的应用程序(最好的教训是通过艰难的方式学习的)。我现在正在学习 rspec 并为该应用编写测试套件。
我为管理员和非管理员设置了控制器权限和查看权限,这在实践中确实有效(我通过彻底的浏览器手动测试知道这一点)。
在这种情况下,我在标题中有一个“管理员”链接,当用户登录并且也是管理员时显示该链接。
我的 StaticPagesController 也有一个有效的 before_action 设置,所以没有人可以访问管理页面,除非他们已经登录并且也是管理员。
我为此编写了一些测试,并认为我已经对其进行了排序,直到我注意到,当对包含这些测试的特定 /features 规范文件进行更改时,guard 只运行这些测试并通过它们。但是,当我运行整个测试套件时,同样的测试会失败。我完全被这个弄糊涂了。
我认为这可能与 Devise 有关,但我只是不知道。
spec/features/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 ==)
对我来说,这意味着管理页面可以访问,而它本来不应该访问。在我的浏览器中,无法看到管理页面链接,也无法通过手动输入 url 访问该页面,除非用户已登录并且是管理员。
运行完整测试套件时,上下文“如果用户是管理员”中的测试全部通过。
spec/factories/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
models/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>
宝石文件:
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'
【问题讨论】:
-
在您的规范中 - 尝试检查用户在管理页面上实际看到/看不到的内容,例如
expect(page).not_to have_content("Welcome Mr Admin")或登录管理页面上实际显示的内容 - 如果您可以看到,那么是的,您需要弄清楚为什么人们会自动登录到管理员。否则可能是他们在管理员登录页面上??? -
另外,风格要点:使用
&&和||而不是and和or(在这里阅读原因:devblog.avdi.org/2010/08/02/using-and-and-or-in-ruby)其次:current_user.admin == true这非常危险。 . 如果您不小心忘记了其中一个=怎么办 - 这是一个很难发现的错误,实际上不会导致您的代码失败……只是为了让所有用户静默地看到管理页面。 总是 这样做是一个好习惯:true == current_user.admin如果你不小心改用=,那将会失败
标签: ruby-on-rails-4 rspec capybara factory-bot