2015-06-09 45 views
2

首先,我應該說雖然我已閱讀了很多關於should_receive的內容,但我仍然不完全確定自己正在理解其背後的概念,所以我正在做的事情可能完全不可能。使用Rspec should_receive來測試控制器是否正確調用對象上的方法

我有以下幾點:

class PlansController 
    def destroy 
    plan = plan.find_by_id(params[:id]) 
    if plan.cancel_stripe_subscription(params[:reason]) 
     flash[:success] = "success" 
     redirect_to root_path 
    else 
     #error handling 
    end 
    end 
end 

class Plan 
    def cancel_stripe_subscription(reason) 
    self.status = "canceled" 
    self.cancellation_reason = reason 
    if self.save 
     return true 
    else 
     return false 
    end 
    end 

在我的控制器的規格,我想這是有道理的做一個測試,該cancel_stripe_subscription方法調用成功(使用1should_receive1),用正確的參數和一切,而另一項測試顯示destroy動作的輸出是正確的。

換句話說,我想寫出下列控制器規格:

describe PlansController, "Destroy Action" do 
    before do 
    @plan = Plan.create(...) 
    end 
    it "should have called destroy action" do 
    delete :destroy, 
     plan: { 
     id: @plan.id, 
     reason: "something" 
     } 
     assigns(:plan).should_receive(:cancel_stripe_subscription).with(reason:"something").exactly(1).times.and_return(true) 
    end 

    it "should have called destroy action" do 
    delete :destroy, 
     plan: { 
     id: @plan.id, 
     reason: "something" 
     } 
    assigns(:plan).status.should == "canceled" 
    assigns(:plan).cancellation_reason.should == "something" 
    end 
end 

第二次測試通過,但第一個拋出

Failure/Error: assigns(:plan).should_receive(:cancel_stripe_subscription) 
    (#<Plan:0x007fe282931310>).cancel_stripe_subscription(*(any args)) 
     expected: 1 time with any arguments 
     received: 0 times with any arguments 

所以,我真的有兩個問題:

  1. 只是爲了確認,我是否正確使用should_receive?我是否應該爲此進行測試?或者第二個測試被普遍接受爲足夠的?
  2. 如果我應該測試這個,使用should_receive的正確方法是什麼? (注意,還沒有運氣expect(@plan).to have_received(:cancel_stripe_subscription)要麼)

回答

1

調用測試方法should_receive預期已被設置;你之後設置它。既然你需要先設置它,那麼你必須確保你設置的期望對象最終在操作中被操作。正常的方法是在Plan存根出find_by_id,像這樣:

it "should have called destroy action" do 
    Plan.stub(:find_by_id).and_return(@plan) 
    assigns(:plan).should_receive(:cancel_stripe_subscription).with(reason:"something").exactly(1).times.and_return(true) 
    delete :destroy, plan: { id: @plan.id, reason: "something" } 
end 

(我假設你的意思是在你的destroy行動的第一行寫plan = Plan.find_by_id(params[:id])

至於是否你應該以這種方式進行測試,我想說你的第二項測試能夠很好地驗證你想要的結果,而且你並不需要去解決所有的問題。

+0

好吧我想我在這裏有點密集,我不完全確定我瞭解它的區別。我的方式和你的方式都先調用'delete:destroy',然後檢查'assigns(:plan).should_receive',這一定不是區別。但是當我使用創建的'@ plan'時,你正在扼殺一個'plan'。這是否意味着一個本質上必須存儲'should_receive'的計劃對象才能工作,否則就會在實際的'@ plan'對象上調用該方法,從而使其不再可測試?我有道理嗎? – james

+0

你很困惑,因爲我犯了一個錯誤,並以錯誤的順序陳述。現在已經修復了。 :) –

+0

哦,這是超級怪異和不舒服,我...我要繼續我的第二次測試=)謝謝! – james

2

我認爲這裏的困惑部分是由於結合了兩種不同風格的測試,mockist(你的第一個測試)和classicist(你的第二個測試)。根據您的首選測試樣式,使用其中一種或者另一種是很好的,但是同時使用它們來測試同一段代碼有點多餘。

+0

這是一個很好的解釋,謝謝! – james

相關問題