2013-07-29 70 views
6

我可以知道如何在控制器創建方法中存根方法嗎?我需要寫這個規範,但我得到了這些錯誤。我需要檢查控制器中的create方法,然後才能在模型中創建新的公司記錄之前執行validate_fbid方法。Rspec:在控制器中的存根方法

錯誤:

1) Companies new company create with valid information#validate_fbid should have correct parameters and return value 
Failure/Error: CompaniesController.create.should_receive(:validates_fbid).with(company) 
NoMethodError: 
    undefined method `create' for CompaniesController:Class 
# ./spec/requests/companies_spec.rb:38:in `block (5 levels) in <top (required)>' 

    2) Companies new company create with valid information#validate_fbid should fbid validation passed 
Failure/Error: CompaniesController.create.stub(:validates_fbid).and_return('companyid') 
NoMethodError: 
    undefined method `create' for CompaniesController:Class 
# ./spec/requests/companies_spec.rb:43:in `block (5 levels) in <top (required)>' 

CompaniesController

def create 
company = Company.new(params[:company]) 
verifyfbid = validate_fbid(company) 

if verifyfbid != false 
    if company.fbid.downcase == verifyfbid.downcase 
     if company.save 
      @message = "New company created." 
      redirect_to root_path 
     else 
      @message = "Company create attempt failed. Please try again." 
      render 'new' 
     end 
    else 
     @message = "Company create attempt failed. Invalid facebook id." 
     render 'new' 
    end 
else 
    @message = "Company create attempt failed. No such facebook id." 
    render 'new'    
    end    
end 

    private 
    def validate_fbid(company) 
    uri = URI("http://graph.facebook.com/" + company.fbid) 
    data = Net::HTTP.get(uri) 
    username = JSON.parse(data)['username']  
    if username.nil? 
    return false 
    else 
    "#{username}" 
    end 
end 

請求/ companies_spec.rb

context "#validate_fbid" do    
     #validate fbid 
     let(:company){ Company.new(name:'Example Company', url: 'www.company.com', fbid: 'companyid', desc: 'Company desc')} 

     it "should have correct parameters and return value" do 
      CompaniesController.create.should_receive(:validates_fbid).with(company) 
           .and_return('companyid') 
     end 

     it "should fbid validation passed" do    
      CompaniesController.create.stub(:validates_fbid).and_return('companyid') 
      company.fbid.should_not be_nil 
      company.fbid.should == 'companyid' 
      company.save 
      expect { click_button submit }.to change(Company, :count).by(1) 
     end            
    end  

回答

15

你不想存根的方法,當它的主題你測試用例

context "#validate_fbid" do 
    #test the function here 
    #don't stub 
end 

當你測試控制器創建操作,您可以存根「validate_fbid」

describe "post create" do 
    ... 
    CompaniesController.any_instance.stub(:validates_fbid).and_return('companyid') 
    ... 
end 

希望它能幫助。

+10

'allow_any_instance_of(CompaniesController)。爲了接收(:validates_fbid)。and_return('companyid')'Rspec3 – ryan2johnson9

5

當代碼很難測試時,通常是因爲它很複雜。

你應該這樣重構這個代碼:

  • 移動驗證邏輯到一具有企業驗證的在Facebook上
  • 單一的責任,這將讓獨立的Web層的驗證功能新的「服務類」和容易測試
  • 服務類化妝規範,這將隔離測試代碼(無控制器)
  • 清理邏輯控制器 - 你不不想有邏輯的控制器(經驗法則:嵌套最大的一個級別)內
  • 規格的控制器會以及

容易控制器代碼可以是這個樣子:

def create 
    company = Company.new(params[:company]) 
    verified = FbCompanyVerifier.new.verify(company) 

    if verified and company.save 
    # success logic 
    else 
    # fail logic 
    end 
end 
+0

嗨,謝謝你的迴應。這是否意味着我需要創建一個具有「新」方法和「驗證」方法的類FbCompanyVerifier?我想我不允許在控制器頁面中創建一個類,以便創建哪個文件夾「FbCompanyVerifier」?感謝您的澄清。我對此很新。 –

+0

更正 - 實例方法爲'verify'的完整類,如果它沒有執行任何操作,則可以跳過'new'(初始化)方法。通常這是一種最佳實踐,因此您可以使用一流對象/普通紅寶石對象進行操作。 (有些人可能會使用類方法,它有自己的優點和缺點)。 由於這個類是依賴於你的模型公司,你應該把它放在應用程序文件夾'app/services'的某個地方。你可以放置小的單一職責類,它包含一個或兩個方法,並獨立於Web層。 – jurglic

+0

您也可以檢查code_climate博客帖子,它描述了類似的重構 - 將邏輯代碼提取到新的類中,並且負責單個責任,編寫簡單的規範,然後在代碼中將其用作PORO(普通舊紅寶石對象)。 : http://blog.codeclimate.com/blog/2013/07/23/testing-code-in-a-rails-initializer/ – jurglic

1

如果要測試控制器,您可以直接訪問控制器:

controller.stub(:message) { 'this is the value to return' } 
+0

這個答案適用於我,因爲當我存根方法時,我知道它需要一個參數,但我不知道參數是什麼。我只想傳遞參數,所以我使用帶參數的塊來覆蓋(存根)方法。 – Volte

相關問題