Rspec,有一个更好的方式来写这个

时间:2011-08-25 00:01:47

标签: ruby-on-rails rspec

在我的系统中,有4类用户:非登录,消费者,制作人和管理员。

我目前正在使用Cancan进行ACL。

在编写rspec时,我看到以下内容:

describe DealsController do 

  describe "non-signed-in users" do
    before(:each) do 
      @deal = Factory(:deal)
    end

    describe "should be able to" do 
      it "access index" do get :index end
      it "show deal" do get :show, :id => @deal end

      after(:each) do
        response.should be_success
      end
    end

    describe "should not be able to" do 
      it "redeem" do get :redeem end
      it "new" do get :new end
      it "edit" do get :edit, :id => @deal end
      it "update" do get :update, :id => @deal end
      it "destroy" do get :destroy, :id => @deal end

      after(:each) do
        response.should_not be_success
        response.should redirect_to(root_path)
        flash[:error].should == "Permission denied."
      end
    end
  end

  describe "consumers" do 
    before(:each) do 
      @user = test_sign_in(Factory(:user, :role => "consumer"))
      @deal = Factory(:deal)
    end

    describe "should be able to" do 
      it "access index" do get :index end
      it "show deal" do get :show, :id => @deal end

      after(:each) do
        response.should be_success
      end
    end

    describe "should not be able to" do 
      it "redeem" do get :redeem end
      it "new" do get :new end
      ...


      after(:each) do
        response.should_not be_success
        response.should redirect_to(root_path)
        flash[:error].should == "Permission denied."
      end
    end
 end

  describe "producer" do 
    before(:each) do 
      @user = test_sign_in(Factory(:user, :role => "producer"))
      @business = Factory(:business, :user_id => @user.id)
      @deal = Factory(:deal, :business_id => @business.id)
    end

    it "should be able to access index" do 
      get :index
      response.should be_success
    end

    describe "in show deals" do 
      it "should be able to see merchant controls for his deal" do 
        get :show, :id => @deal
        response.should have_selector('h3', :content => "Merchant Controls")
      end

      it "should not be able to see merchant controls for other's deal" do 
        @user2 = Factory(:user, :role => "producer")
        @business2 = Factory(:business, :user_id => @user2.id)
        @deal2 = Factory(:deal, :business_id => @business2.id)
        get :show, :id => @deal2

        response.should_not have_selector('h3', :content => "Merchant Controls")
      end
    end

    describe "should not be able to" do 
      it "new" do get :new end
      ...

      after(:each) do
        response.should_not be_success
        response.should redirect_to(root_path)
        flash[:error].should == "Permission denied."
      end
    end
 end

end

我甚至没有填写管理部分,但我非常确信这不是推荐的方法。

什么是更好的方法?

2 个答案:

答案 0 :(得分:4)

您应该考虑为单行采用新风格。例如。做这样的事

describe "should be able to" do
  it "access index" { get :index }
  it "show deal"    { get :show, :id => @deal }
  after(:each)      { response.should be_success }
end

此外,您可以考虑为重复的多行预期创建自定义匹配器。例如,以下内容:

after(:each) do
  response.should_not be_success
  response.should redirect_to(root_path)
  flash[:error].should == flash
end

可以替换为

after(:each) { response.should fail_redirect_and_flash(root_path, 'Permission denied.') }

使用以下自定义匹配器代码

RSpec::Matchers.define :fail_redirect_and_flash do |path,flash|
  match do |response|
    response.should_not be_success
    response.should redirect_to(path)
    flash[:error].should == flash
  end
end

此外,许多人甚至不打算编写控制器单元测试,因为设计良好的控制器通常只有很少的代码(他们通常只使用模型上的方法设置一些变量并渲染/重定向,因此大多数测试实际发生在模型中)。相反,它们将控制器和视图测试一起包装并使用黄瓜。你仍然会遇到同样丑陋的代码,但有些人发现它更容易管理。

就此而言,你会发现你的规范“应该无法看到其他交易的商家控件”实际上是测试你的观点,而不是你的控制器。您可能应该删除控制器中使用的任何逻辑来显示商家控件并将其粘贴在帮助器中并单独测试。这有助于保持控制器的精简。例如。您可以拥有在视图中使用的以下帮助程序

def merchant_controls(deal, business)
  if business.can? :update, deal
    # render html
  end
end

你可以为这个助手方法制定一个规范......

Describe "merchant_controls(deal, business)" do
  before(:all) do
    @business_a = create(:business)
    @deal_a = create(:deal, :business_id => @business_a)
    @business_b = create(:business)
    @deal_b = create(:deal, :business_id => @business_b)
  end

  it "a business should see merchant controls next to its own deals" do
    merchant_controls(@business_a, @deal_a).should eq("blahblah")
  end
  it "a business should not see merchant controls next to other business' deals" do
    merchant_controls(@business_a, @deal_b).should eq("")
  end
end

希望这有帮助。

答案 1 :(得分:0)

老实说,如果它是你关心的应用程序 - 特别是如果它涉及用户交互 - 有大量的验收测试是好的。显然,不要被带走,并开始测试像某些颜色可见的东西。但如果你没有经过彻底的测试,你就不会成为程序员。

您可能希望将测试分解为更易于管理的测试。也许为四种用户类型中的每一种创建单独的物理文件。我不记得了,但我认为这不会引起RSpec的任何问题。

就我个人而言,我认为使用RSpec进行验收测试太可怕了。太砂砾了。我更喜欢使用黄瓜(http://www.cukes.info)。它使验收测试更容易,特别是如果你也想测试javascript。在我看来,他们写作更快,更干净。如果我是你,看看它是否适合你,我会稍微看一下。