1

问题

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

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

我相信这个案例对于那些试图缩短他的测试脚本文件的人来说可能会很有趣,同时拥有许多字段,并且可以完全控制显示的内容和不显示的内容,所以我会努力寻找解决方案,如果你愿意,你的帮助:)

表单视图

没有什么花哨

= 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 的内部结构会对我造成不良影响,但我无法弄清楚,我对这些还不是很了解。谢谢阅读

4

2 回答 2

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

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

于 2013-07-23T13:29:05.833 回答
1

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

= form_for @user do |f|

并且所有方法都传递给 object f。现在f.object将指向@user,但它是副本@user还是@user自身,我不知道。所以我希望这User.any_instance应该有效。

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

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

于 2013-07-24T09:14:59.917 回答