如何编写符合Rubocop规范的Rails视图RSpec及相关疑问
我用rails generate scaffold生成了控制器、模型、视图及对应的RSpec测试,但生成的视图RSpec存在共性问题。生成的代码如下:
require 'rails_helper' RSpec.describe 'import_files/new', type: :view do before do assign(:import_file, build(:import_file)) end it 'renders new import_file form' do render assert_select 'form[action=?][method=?]', import_files_path, 'post' do assert_select 'input[name=?]', 'import_file[path]' assert_select 'select[name=?]', 'import_file[file_type]' end end end
Rubocop先是提示示例中没有断言,我把assert_select添加到.rubocop.yml的期望列表(配置如下):
RSpec: Language: Expectations: - assert_select
之后又被提示存在3个断言。RSpec风格指南的类似结构示例也被Rubocop标记为多断言,我疑惑是否应该禁用视图中的RSpec/MultipleExpectations规则。另外还有两个问题:
render后可用什么方法替代select_out_just_the_form来选择表单及其子元素且不添加断言?- 是否可以重构代码让
render仅执行一次(如示例代码),但担心表单渲染失败会导致多个无关错误?
是否禁用RSpec/MultipleExpectations规则
视图测试的核心是验证页面结构完整性,比如表单的动作、方法,以及内部输入控件是否存在,这类场景下多个断言是合理的。RSpec官方风格指南的类似示例也认可这种写法,因此可以针对视图测试目录单独禁用该规则,在.rubocop.yml中添加:
RSpec/MultipleExpectations: Exclude: - spec/views/**/*
这样既保留了其他测试文件的多断言检查,又适配了视图测试的实际需求。
替代select_out_just_the_form的方法
可以使用Rails视图测试内置的css_select方法,它仅返回匹配的元素,不会触发断言。用法示例:
it 'renders new import_file form' do render form = css_select 'form[action=?][method=?]', import_files_path, 'post' # 基于返回的Nokogiri节点集合检查子元素 expect(form.css('input[name="import_file[path]"]')).to be_present expect(form.css('select[name="import_file[file_type]"]')).to be_present end
css_select返回Nokogiri节点集合,你可以用css、xpath等Nokogiri API进一步筛选子元素,全程由你手动控制验证逻辑,不会自动添加断言。
关于render仅执行一次的问题
你的示例代码中render已经只执行一次,这种写法是可行的。虽然表单渲染失败会导致多个断言失败,但这正是视图测试的价值——一次性暴露页面结构的所有问题。拆分多个测试用例重复渲染反而会增加执行时间,且视图渲染失败本身是严重问题,所有依赖该结果的断言失败都是相关的,并非“无关错误”。
如果想兼顾单断言要求和单次渲染,可以把render放到before块中,拆分测试用例分别验证单个点:
RSpec.describe 'import_files/new', type: :view do before do assign(:import_file, build(:import_file)) render end it 'renders form with correct action and method' do assert_select 'form[action=?][method=?]', import_files_path, 'post' end it 'includes path input field' do assert_select 'input[name=?]', 'import_file[path]' end it 'includes file type select field' do assert_select 'select[name=?]', 'import_file[file_type]' end end
这种写法既满足Rubocop的单断言要求,又保证render仅执行一次,同时每个测试职责更单一,失败信息更明确。
内容的提问来源于stack exchange,提问作者pedz

