2011-04-22 81 views
2

我使用Devise進行身份驗證明顯,我想要做的就是測試一個方法在用戶對象上調用。所以,我的規格如下所示:設計,Rspec,用戶期望

it "should retrieve something for user" do 
    @user = Factory.create(:user) 
    sign_in @user 
    @user.expects(:something) 
    get :manage 
end 

我是希望這個問題失敗,除非我做的:

it "should retrieve something for user" do 
    @user = Factory.create(:user) 
    sign_in @user 
    controller.stubs(:current_user).returns @user 
    @user.expects(:something) 
    get :manage 
end 

僞造出來的控制器上的CURRENT_USER通話似乎做作,如果sign_in @user是有一項測試助手。在調試器中挖掘之後,它也出現在不僞造current_user時,@user真的被返回,所以我不知道爲什麼不符合期望。

想法?

回答

3

當前用戶在存儲在會話中之前被序列化;可能,對於ActiveRecord的用戶模型,它存儲了用戶的ID:

https://github.com/plataformatec/devise/blob/master/lib/devise/test_helpers.rb#L49

這意味着,當你的控制器重新獲取其用戶:

https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/helpers.rb#L47-49

它找到USER_ID會話,並從數據庫中重新獲取用戶。

這意味着控制器中#current_user返回的User對象與您在測試中傳遞給@sign_in的@user不同的Ruby對象,因此一個對象的存根和期望不會附加到另一個對象上。

我還沒有廣泛使用Devise/Warden,但我很確定這是怎麼回事。您可以嘗試在兩個實例上打印出#object_id以確認這一點。

挑剔的語義更新2014年2月:

控制器上打樁#current_user是一個很好的辦法在這裏,被測取決於你的系統的「邊界」的定義(SUT) - 即控制器。假設Devise定義了#current_user方法,並通過Devise將它們混合到您的控制器中,您可能會認爲#current_user是SUT的外部因素,因此適用於存根的公平遊戲。

您也可以對Devise正在訪問的基礎層(ActiveRecord)進行存根,例如通過將User.find存檔以返回您的@user對象。這意味着你的規範正在測試你的動作實現以及#current_user的Devise實現,所以如果這些事情中的任何一個稍後改變,規範將會失敗。

假設設計使用User.find(args),並說您存根該方法,然後更高版本的設計更改爲使用User.where(args).first() - 您的代碼沒有更改,但底層庫,並且您的規格失敗。關於這個想法的一般想法,有時候你可能會喜歡這種行爲(考慮用例如WebMock嘲笑原始HTTP響應,而不是剔除Net::HTTP方法,以便以後可以交換http庫),並且你不會也許這個設計問題算作一個)。

+0

經過一番深入挖掘,看來你是對的!謝謝(你的)信息。 – 2011-04-22 17:46:26

+0

最後一個解決方案,經過許多小時的搜索..謝謝! – lambinator 2011-05-27 07:09:23