【问题标题】:Rspec2 + Rails4: Testing for displayed model fields in form partialRspec2 + Rails4:以部分形式测试显示的模型字段
【发布时间】:2013-07-23 09:23:34
【问题描述】:

问题

我想编写测试来检查显示在“显示”和“表单”部分中的模型字段。我成功的是“表演”,而不是“形式”。

主要约束:解决方案必须能够循环遍历包含模型字段的每个名称的数组。

我相信这个案例对于任何试图缩短其测试脚本文件、同时拥有许多字段并完全控制显示的内容和不显示的内容的人来说可能会很有趣,所以我会努力寻找一个解决方案,请在您的帮助下:)

表单视图

没有什么花哨的

= form_for @user do |f|
  = f.select :field_1, options_from_collection_for_select ...
  = f.text_field :field_2
  ...

实际情况

我为“显示”部分找到了一种简单的方法,这是我的规范文件的样子:

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 输出到控制台,以手动检查我的视图生成的内容,是的,这些字段是正确生成的。

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 的内部结构对我不利,但我无法弄清楚,我对这些还不是很了解。感谢阅读

【问题讨论】:

  • 如果将user.should_receive 更改为User.any_instance.should_receive 会发生什么?在这种情况下测试是否有效?
  • 一个更神秘的输出消息,但仍然没有通过
  • 嗯...这很有趣。你能显示查看代码吗?另外,你确定你在代码中调用first_namelast_name 吗?也许有类似user.attributes.slice(*current_role.allowed_user_attrbutes).each { |k,v| ... }的代码?
  • 我刚刚编辑了我的问题,我会在几分钟内添加您提出的问题。感谢您的帮助
  • 我在上次编辑期间解析了部分视图代码。真的没有什么花哨的,我有一些辅助方法,但它们只将我的字段包装在 div 和标签中,准备传递给表单构建器的选项哈希等......在链的末端,它是 Rails FormBuilder 做所有的工作.没有打电话给模特attributes,也没有类似的东西。我没有扩展 FormBuilder 的 gem 或自定义类。

标签: ruby-on-rails rspec2 ruby-on-rails-4 rspec-rails


【解决方案1】:

我通常尝试尽可能简单地编写单元测试。单元测试中的循环不会增加太多可读性,并且通常不是很好的实践。我会这样重写测试:

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

为了使这些测试更加可靠,请确保用户工厂不包含重复数据。

【讨论】:

  • 感谢您的回答。这是一个很好的方法。我用附加信息编辑了我的问题,解释了为什么我试图用循环压缩我的代码。我会等着看是否有人想出比你更“自动”的解决方案。
  • 好的,看到了。是的,我已经考虑过这一点(你确实用你的原始答案给了我这个想法),我会把它作为备用计划,但会进一步挖掘,我希望有一个不依赖于价值的解决方案跨度>
  • 决定:) 我会采用你的方法,你们说服了我。非常感谢您的宝贵时间
【解决方案2】:

我不太确定您是如何构建表单的,但如果您使用 form_forsimple_form_forformtastic_form_for 助手,实际上您使用的是不同的对象。你写类似(假设基本的form_for

= form_for @user do |f|

所有方法都被转发到对象f。现在f.object 将指向@user,但它是@user 还是@user 本身的副本,我不知道。所以我希望User.any_instance 应该可以工作。

无论如何,在进行视图测试时,如何设置字段的内容并不重要,重要的是正确设置字段的内容。假设您自己构建表单,然后切换到另一个库来构建表单,并且您的所有测试都中断了,因为它以不同的方式检索数据。这应该没关系。

所以我支持@DNNX,在您的视图测试中,您应该测试呈现的 HTML 的内容,而不是如何检索数据。

【讨论】:

  • 好吧,很明显,我会听从你们的建议,并依赖我测试中的值。感谢您的时间和建议 :) 干杯
猜你喜欢
  • 1970-01-01
  • 2022-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 1970-01-01
相关资源
最近更新 更多