ruby-on-rails - 简化和正确的 RSpec Controller 测试

标签 ruby-on-rails ruby-on-rails-3 rspec tdd bdd

我有一些 RSpec Controller 测试。有些有效,有些无效,我正在尝试找出到底如何修复它们并提高它们的效率

理想情况下,我想看看是否可以将每个规范转化为以下形式

subject { ... }
  it { ... }
  it { ... }
  it { ... }

请注意,对于我的所有 Controller 规范,我都为实际 Controller 操作编写了宏。这些宏都经过测试并且都可以工作,并且它们的名称使它们的作用相当明显。

我的“创建”测试:

formats ||= ["html", "js"]
formats.each do |format|
  context "valid attributes" do
    subject { do_post_create( :customer, valid_attributes, format ) }
      its(:response_code) { should eq(302)}
      it { should redirect_to admin_customer_path(Customer.find_by_id(???))}
      it { expect { subject }.to change(Customer, :count).by(1) }
  end

  context "invalid attributes" do
    subject { do_post_create( :customer, invalid_attributes, format ) }
      its(:response_code) { should eq(200)}
      it { should render_template :new }
      it { expect { subject }.to_not change(Customer, :count).by(1) }
  end
end

在该规范中,我一直在尝试找出某种方法来从 post 语句中获取新创建的对象的 ID。我尝试过“Customer.last”,但这似乎不起作用。有什么想法吗?

我的“更新”规范:

formats ||= ["html", "js"]
formats.each do |format|
  context "valid attributes" do
    let(:object) { FactoryGirl.create(:customer) }
    subject { do_put_update( class_to_symbol(model), object.id, attributes, format ) }
      its(:response_code) { should eq(302)}

    it "does alter #{model}" do
      do_put_update( class_to_symbol(model), object.id, attributes, format )
      assigns(:customer).should eq(object)
      flash[:notice].should =~ /Success/
      object.reload
      attributes.each do |key, value|
        object.send(key.to_s).should eq(value)
      end
    end
  end
  context "invalid attributes" do
    let(:object) { FactoryGirl.create("customer") }
    let(:invalid_attributes) { {:username => "!"} }
    subject { do_put_update( class_to_symbol(model), object.id, invalid_attributes, format ) }
      its(:response_code) { should eq(200)}

    it "does not alter #{model}" do
      do_put_update( class_to_symbol(model), object.id, invalid_attributes, format )
      assigns(:customer).should eq(object)
      flash[:notice].should =~ /Fail/
      object.reload
      attributes.each do |key, value|
        object.send(key.to_s).should_not eq(value)
      end
    end
  end
end

在更新测试中,我想尝试以更简洁的方式表达第二个 block ,理想情况下我可以对所有测试使用相同的“主题”语句。这可能吗?

最佳答案

我认为您对这些规范考虑过度了。不要试图强制每个规范都采用预定义的格式(subject/it/...),而是编写规范,以便它们清楚地记录应该发生的情况,然后 之后尝试重构代码。

恰当的例子: Controller 操作使用隐式subjectsubjectits 旨在与对象一起使用,而不是与方法一起使用,并且只有以这种方式使用时才真正有意义。例如,这是有道理的:

subject { [1, 2, 3, 4] }
its(:size) { should == 4 }

在这里,测试的内容非常清楚:4 元素数组的大小为 4。

但是,当你写:

subject { do_post_create( :customer, valid_attributes, format ) }
its(:response_code) { should eq(302)}

在不检查 do_post_create 操作的情况下,并不清楚您从哪里获取响应代码。您说宏的名称“使它们的作用变得相当明显”,但它们并没有使它们将返回什么变得相当明显,这是使用隐式主题的关键,因为它是成为主题的返回值。

这样写会更清楚:

it "responds with a 302" do
  do_post_create(:customer, valid_attributes, format)
  response.should eq(302)
end

我也不建议混合带有或不带有隐式主题的规范,因为这会使您实际测试的内容更加困惑。例如,在无效属性 context block 中,您设置了一个主题,但随后在第二个规范中您实际测试了 customer 的分配 (assigns(:customer) .should eq(object)),所以基本上这个主题与这个测试无关。 (但是,通过在此处设置主题然后不使用它,您实际上发送了两次 PUT 请求(通过 do_put_update),这必然会导致问题 - 再次,另一个原因不在 subject block 中发出请求。)

我可以继续说下去,但我想你已经明白了。如果你能在不影响可读性的情况下做到这一点,那么让规范变得简短而甜蜜是很棒的,但在这种情况下,我认为你做得太过分了。

只是我的两分钱,希望有帮助。

附:如果上面的观点看起来有点极端,请阅读 documentation for implicit subjects ,您会发现他们实际上建议不要在面向公众的测试中使用隐式主题:

While the examples below demonstrate how subject can be used as a user-facing concept, we recommend that you reserve it for support of custom matchers and/or extension libraries that hide its use from examples.

关于ruby-on-rails - 简化和正确的 RSpec Controller 测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13121205/

相关文章:

ruby-on-rails - 创建资源后如何重置我选择的选择菜单?

ruby-on-rails - Rails 3,多对多形式使用accepts_nested_attributes_for,我该如何正确设置?

ruby-on-rails - 禁用 Rails 中的单元测试生成器

ruby-on-rails - 从错误的完整错误消息中删除前缀属性名称[:base]

ruby-on-rails - rails 中的动态路线

ruby-on-rails - 可锁定设计-如何使用unlock_in解锁帐户

ruby-on-rails - 为什么要坚持?没有察觉到数据库的变化?在 rails 上

ruby-on-rails - 使用 Ruby 1.9.3 的 Windows 8 x64 上的 nokogiri gem 缺少 libxml2

mysql - 导轨 : Querying with an Array

ruby-on-rails - Rspec,确保在 Rails Controller 类上调用方法