【问题标题】:Simplifying and correct RSpec controller tests简化和纠正 RSpec 控制器测试
【发布时间】:2012-10-29 11:59:34
【问题描述】:

我有一些 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

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

【问题讨论】:

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


    【解决方案1】:

    我认为你在考虑这些规格。与其尝试将每个规范强制转换为预定义格式 (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 操作,您从哪里获取响应代码并不清楚。您说宏的名称“使它们的作用相当明显”,但它们并没有使其相当明显它们将返回什么,这是使用隐式主题的关键,因为它是成为主题的返回值。

    这样写会更清楚:

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

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

    我可以继续,但我想你明白了。如果您可以在不影响可读性的情况下使规范简短而有趣,那就太好了,但是在这种情况下,我认为您已经过火了。

    就我的两分钱,希望对你有帮助。

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

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

    【讨论】:

    • Hrm,这些都是非常好的观点 - 认为我深信不疑。顺便说一句,为什么“Customer.last”在我的规范中会返回零?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多