问题
我想编写测试来检查我的“show”和“form”部分中显示的模型字段。我成功了“表演”,而不是“形式”。
主要约束:解决方案必须能够遍历包含模型字段的每个名称的数组。
我相信这个案例对于那些试图缩短他的测试脚本文件,同时拥有多个字段,并且完全控制显示内容和不显示内容的人来说非常有趣,所以我会努力寻找一个解决方案,如果您愿意,请帮助您:)
表单视图
没什么好看的
= form_for @user do |f|
= f.select :field_1, options_from_collection_for_select ...
= f.text_field :field_2
...
实际情况
我找到了一个简单的“show”部分方法,这是我的spec文件的样子:
def user_fields_in_show_view
[:field_1, :field_2, ..., :field_n]
end
it 'display fields' do
user_fields_in_show_view.each do |field|
User.any_instance.should_receive(field).at_least(1).and_call_original
end
render
end
这很有效。
-
但完全相同的技术在“形式”部分中不起作用,使用相同的代码
def user_fields_in_form_view # List of fields need to be different, because of the associations
[:field_1_id, :field_2, ..., :field_n]
end
it 'display fields' do
user_fields_in_form_view.each do |field|
User.any_instance.should_receive(field).at_least(1).and_call_original
end
render
end
它像这样抱怨:
Failure/Error: Unable to find matching line from backtrace
Exactly one instance should have received the following message(s) but didn't: field1_id, field_2, ..., field_n
# Backtrace is long and shows only rspec/mock, rspec/core, rspec/rails/adapters, and spork files
到目前为止我尝试了什么
1 - 我将测试的存根部分注释掉并输出rendered
到控制台,手动检查我的视图生成了什么,是的,字段是否正确生成。< / p>
2 - 我用我分配给视图的模型替换了User.any_instance
,错误略有不同,但仍无法正常工作
it 'display fields' do
user = create :user
assign :user, user
user_fields_in_form_view.each do |field|
user.should_receive(field).at_least(1).and_call_original
end
render
end
给出:
Failure/Error: user.should_receive(field).at_least(1).and_call_original
(#<User:0x0000000506e3e8>).field_1_id(any args)
expected: at least 1 time with any arguments
received: 0 times with any arguments
3 - 我更改了代码,因此it
位于循环内部,如下所示:
user_fields_in_form_view.each do |field|
it 'display fields' do
user = create :user
assign :user, user
user.should_receive(field).at_least(1).and_call_original
render
end
end
与上述结果相同
我没有选择。我怀疑FormBuilder的内部对我起了不好的伎俩,但我无法弄明白,我对这些并不是很了解。感谢您的阅读
答案 0 :(得分:2)
我经常尝试尽可能简单地编写单元测试。单元测试中的循环不会增加很多可读性,并且通常不是很好的练习。我会像这样重写测试:
it 'should display user name and email' do
# note: `build` is used here instead of `create`
assign :user, build(:user, first_name: 'John', last_name: 'Doe', email: 'jd@example.com')
render
rendered.should have_content 'John'
rendered.should have_content 'Doe'
rendered.should have_content 'jd@example.com'
end
因此,我们并没有限制它应该如何呈现名字和姓氏的观点。例如,如果我们的视图使用以下(坏)代码来呈现用户的全名,那么您的测试将失败,但我的测试将正常工作,因为它测试视图的行为,而不是其内部:
<%= user.attributes.values_at('first_name', 'middle_name').compact.join(' ') %>
此外,一次测试中的多个断言也是一种难闻的气味。更进一步,我将用三个较小的测试替换这个测试:
it "should display user's first name" do
assign :user, build(:user, first_name: 'John')
render
expect(rendered).to include 'John'
end
it "should display user's last name" do
assign :user, build(:user, last_name: 'Doe')
render
expect(rendered).to include 'Doe'
end
it "should display user's email" do
assign :user, build(:user, email: 'jd@example.com')
render
expect(rendered).to include 'jd@example.com'
end
======
UPD :让我们更加动态,以避免大量重复。 Tis没有回答为什么你的规范失败,但希望代表工作测试:
%i(first_name last_name email).each do |field|
it "should display user's #{field}" do
user = build(:user)
assign :user, user
render
expect(rendered).to include user.public_send(field)
end
end
为了使这些测试更可靠,请确保用户工厂不包含重复数据。
答案 1 :(得分:1)
我不太确定如何构建表单,但如果您使用form_for
,simple_form_for
或formtastic_form_for
帮助程序,实际上您使用的是其他对象。你写的东西(假设基本的form_for
)
= form_for @user do |f|
并且所有方法都被转发到对象f
。现在f.object
将指向@user
,但它本身就是@user
或@user
的副本,我不知道。所以我希望User.any_instance
能够发挥作用。
无论如何,在进行视图测试时,如何设置字段内容并不重要,正确设置字段内容非常重要。假设您自己构建表单,切换到另一个库来构建表单,并且所有测试都会中断,因为它会以不同方式检索数据。这应该不重要。
所以我使用@DNNX,在您的视图测试中,您应该测试呈现的HTML的内容,而不是如何检索数据。