如何用Ruby、Capybara、SitePrism高效测试权限差异化页面?
嘿,这个场景我太熟了!之前帮团队搭建SitePrism测试框架时刚好遇到过类似的权限隔离问题,完全不用写两套重复的页面对象,咱们可以用继承+模块混入+动态域名配置这几个SitePrism的特性来完美解决,具体给你几个实用方案:
方案1:基础页面对象+权限专属模块(最推荐)
这个方案的核心是把通用页面逻辑抽成父类,权限专属的元素/方法放到独立模块里,通过继承+混入来组合出不同权限的页面对象,同时动态配置域名。
# 通用基础页面对象:所有页面都继承它,管理共用元素和方法 class BasePage < SitePrism::Page # 动态设置域名的类方法 def self.set_domain(domain) @domain = domain end # 统一生成页面URL def self.url(path = "") "https://#{@domain}#{path}" end # 通用元素:所有用户都能看到的内容 element :navbar, '#main-navbar' element :logout_button, '#logout' # 通用方法 def logout logout_button.click end end # 管理员专属功能模块:只包含管理员可见的元素和方法 module AdminFeatures def self.included(base) base.class_eval do # 管理员专属元素 element :admin_menu, '#admin-menu' element :user_management_link, '#user-management' end end # 管理员专属操作方法 def navigate_to_user_management admin_menu.click user_management_link.click end end # 管理员页面对象:继承通用基础页+混入管理员模块 class AdminDashboard < BasePage include AdminFeatures set_domain 'admin.example.com' # 管理员页面特有的额外元素(如果有) end # 普通用户页面对象:只继承通用基础页 class UserDashboard < BasePage set_domain 'example.com' # 普通用户专属元素 element :profile_link, '#profile' end
使用时,测试用例直接实例化对应权限的页面即可,完全复用通用逻辑:
# 管理员测试场景 RSpec.describe 'Admin Dashboard' do let(:admin_dashboard) { AdminDashboard.new } before do login_as_admin # 自定义的管理员登录方法 admin_dashboard.load end it 'displays admin-only menu and allows user management navigation' do expect(admin_dashboard.admin_menu).to be_visible admin_dashboard.navigate_to_user_management expect(page).to have_current_path('/admin/users') end end # 普通用户测试场景 RSpec.describe 'User Dashboard' do let(:user_dashboard) { UserDashboard.new } before do login_as_user # 自定义的普通用户登录方法 user_dashboard.load end it 'does NOT display admin menu and shows profile link' do expect(user_dashboard).not_to have_admin_menu # 普通用户页面无此元素,直接断言不存在 expect(user_dashboard.profile_link).to be_visible end end
这个方案的优势是职责清晰:通用逻辑集中维护,权限差异隔离在模块里,后续新增权限角色(比如编辑者)时,只要加个EditorFeatures模块再建对应的页面对象就行,扩展性极强。
方案2:可选元素+动态判断(适合差异极小的场景)
如果权限差异只有个别元素的显示/隐藏,完全可以在同一个基础页里用SitePrism的optional_element来标记可选元素,再结合动态域名配置:
class BasePage < SitePrism::Page def self.set_domain(domain) @domain = domain end def self.url(path = "") "https://#{@domain}#{path}" end # 通用元素 element :navbar, '#main-navbar' # 可选元素:不存在时不会抛出错误 optional_element :admin_menu, '#admin-menu' # 判断管理员菜单是否可见 def admin_access? admin_menu.present? end end # 管理员页面 class AdminDashboard < BasePage set_domain 'admin.example.com' end # 普通用户页面 class UserDashboard < BasePage set_domain 'example.com' end
测试时直接断言admin_access?的结果即可,这个方案适合差异非常小的场景,代码量更少,但如果后续权限差异变大,维护成本会上升。
方案3:动态加载Section(适合局部结构差异)
如果页面大部分结构相同,只有某个区块(比如导航栏)有差异,可以用SitePrism的Section特性,根据域名动态加载对应Section:
# 通用导航栏Section class NavbarSection < SitePrism::Section element :home_link, '#home' end # 管理员专属导航栏Section(继承通用导航栏) class AdminNavbarSection < NavbarSection element :admin_menu, '#admin-menu' end class BasePage < SitePrism::Page def initialize(domain) @domain = domain super(url: "https://#{domain}") # 根据域名动态加载对应导航栏Section if @domain == 'admin.example.com' section :navbar, AdminNavbarSection, '#main-navbar' else section :navbar, NavbarSection, '#main-navbar' end end end
测试时实例化页面传入域名即可:
admin_dashboard = BasePage.new('admin.example.com') user_dashboard = BasePage.new('example.com') expect(admin_dashboard.navbar.admin_menu).to be_visible expect(user_dashboard.navbar).not_to have_admin_menu
这个方案适合页面局部结构有差异的场景,保持了页面的统一性。
内容的提问来源于stack exchange,提问作者Jakub Smolar
相关产品推荐
相关产品推荐

