简化和纠正RSpec控制器测试

时间:2012-10-29 11:59:35

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

我有一些RSpec控制器测试。一些工作,一些不工作,我试图弄清楚如何在地球上修复它们并使它们更有效

理想情况下,我想看看我是否可以将每个规格变成以下形式

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

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

我的“创建”测试:

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

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

1 个答案:

答案 0 :(得分:3)

我认为你过度思考这些规格。而不是试图强制每个规范进入预定义的格式(subject / it / ...)编写规范,以便他们清楚地记录应该发生的事情,然后尝试之后重构代码。

一个例子:使用隐式subject进行控制器操作。 subjectits旨在与对象一起使用,而不是方法,并且只有在以这种方式使用时才真正有意义。例如,这是有道理的:

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操作的情况下,您从哪里获得响应代码并不是很清楚。你说宏的名称和#34;使他们做的很明显",但是他们没有明白他们将返回什么,这是关键用于使用隐式主题,因为它是成为主语的返回值。

写下来会更清楚:

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

我也不建议在有和没有隐含主题的情况下混合规格,因为它使你实际测试的内容更加混乱。例如,在您的无效属性context块中,您设置了主题,但在第二个规范中,您实际上测试了customerassigns(:customer).should eq(object))的分配,因此基本上该主题与这个测试。 (但是通过在此处设置主题然后不使用它,您实际上发送了一个PUT请求两次(通过do_put_update),这必然会导致问题 - 再次,另一个原因不是在subject块中发出请求。)

我可以继续,但我认为你得到了照片。如果你能在不损害可读性的情况下做到这一点,那么制作简短和甜美的规格是很好的,但在这种情况下,我认为你已经过火了。

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

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

  

虽然下面的示例演示了如何将主题用作面向用户的概念,但我们建议您保留主题以支持自定义匹配器和/或扩展库,以隐藏其用例。