2012-06-12 10 views
14

注:我讀過this問題和答案,但由於某些原因的代碼是不是爲我工作。 (請參閱下面的錯誤我得到)從Rails的教程第9章RSpec的測試方法破壞(Rails的3.2教程第9章,出埃及記10。)

練習10問你:修改破壞作用[用戶],以防止管理員用戶從毀滅自己。 (第一編寫一個測試。)

這裏棘手的部分是測試它,因爲應用程序已經隱藏了「刪除」鏈接,當前用戶,所以你要直接做的http請求。

我得到的代碼工作,並通過移除一段代碼隱藏刪除鏈接,當前用戶進行了測試。果然,如果我點擊當前登錄用戶的刪除鏈接,它會重定向我,並向我發送通知消息。

從users_controller.rb

def destroy 
    @user = User.find(params[:id]) 
    if current_user?(@user) 
     redirect_to users_path, notice: "You can't destroy yourself." 
    else 
     @user.destroy 
     flash[:success] = "User destroyed." 
     redirect_to users_path 
    end 
    end 

我遇到的問題是在寫這個測試將發送刪除請求,並調用destroy方法。我試圖解決從Rspec test for destroy if no delete link,這我在這裏複製:

從user_pages_spec.rb

describe "destroy" do 
    let(:admin) { FactoryGirl.create(:admin) } 

    it "should not allow the admin to delete herself" do 
     sign_in admin 
     #expect { delete user_path(admin), method: :delete }.should change(User, :count) 
     expect { delete :destroy, :id => admin.id }.should_not change(User, :count) 
    end 
    end 

但是當我運行它,我得到的RSpec

Failures: 

    1) User Pages destroy should not allow the admin to delete herself 
    Failure/Error: expect { delete :destroy, :id => admin.id }.should_not change(User, :count) 
    ArgumentError: 
     bad argument (expected URI object or URI string) 
    # ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>' 
    # ./spec/requests/user_pages_spec.rb:180:in `block (3 levels) in <top (required)>' 

此錯誤所以,我的問題是: 1)爲什麼上面的代碼失敗? 2)如何模擬爲了調用在我的控制器的破壞行動「刪除」?

環境: 的Mac OSX 紅寶石1.9.3p194 的Rails 3.2.3

寶石來進行測試:
組:測試做 寶石 'RSpec的護欄', '2.9.0' 寶石「水豚」, '1.1.2' 寶石 'RB-fsevent', '0.4.3.1',:需要=>假 寶石 '咆哮', '1.0.3' 寶石 '護叉勺', '0.3.2' 寶石 '叉勺', '0.9.0' 寶石 'factory_girl_rails', '1.4.0' 端

更多信息 我已經試過了方式嘗試模擬點擊刪除鏈接並沒有似乎工作。我一直在使用調試器gem來查看destroy方法是否被調用。在點擊鏈接刪除不同用戶測試,destroy方法被調用,它工作正常:

it "should be able to delete another user" do 
    expect { click_link('delete') }.to change(User, :count).by(-1) 
end 

但沒有什麼我試圖生成刪除請求直接一直致力於調用destroy方法。

感謝您的幫助!

請問

**更新**

我試圖DVG的建議:

describe "destroy" do 
    let(:admin) { FactoryGirl.create(:admin) } 

    it "should not allow the admin to delete herself" do 
     sign_in admin 
     #expect { delete user_path(admin), method: :delete }.should change(User, :count) 
     expect { delete :destroy, :id => admin }.to_not change(User, :count) 
    end 
    end 

而得到這個錯誤:

6) User Pages destroy should not allow the admin to delete herself 
    Failure/Error: expect { delete :destroy, :id => admin }.to_not change(User, :count) 
    ArgumentError: 
     bad argument (expected URI object or URI string) 
    # ./spec/requests/user_pages_spec.rb:190:in `block (4 levels) in <top (required)>' 
    # ./spec/requests/user_pages_spec.rb:190:in `block (3 levels) in <top (required)>' 

SOLUTION

我在FOREVER之後找到了它。

我不得不使用Rack :: Test來發出DELETE請求,但是Capybara和Rack :: Test沒有共享相同的MockSession,所以我不得不使用:remember_token和:!sample_app_session cookies並將它們手動進入DELETE請求。這是什麼工作。 (我有其他的問題,下面列出的是我有沒有讓我的破壞作用得到稱爲force_ssl聲明。

describe "destroy" do 
    let!(:admin) { FactoryGirl.create(:admin) } 

    before do 
     sign_in admin 
    end 

    it "should delete a normal user" do 
     user = FactoryGirl.create(:user) 
     expect { delete user_path(user), {}, 
     'HTTP_COOKIE' => "remember_token=#{admin.remember_token}, 
     #{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }. 
     to change(User, :count).by(-1) 
    end 

    it "should not allow the admin to delete herself" do 
     expect { delete user_path(admin), {}, 
     'HTTP_COOKIE' => "remember_token=#{admin.remember_token}, 
     #{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }. 
     to_not change(User, :count) 
    end 
    end 

我有一個force_ssl聲明我在我的users_controller.rb這before_filters後莫名其妙地被扔東西了,所以我從來沒有銷燬行動。

class UsersController < ApplicationController 
    before_filter :signed_in_user, only: [:edit, :update, :index] 
    before_filter :existing_user, only: [:new, :create] 
    before_filter :correct_user, only: [:edit, :update] 
    before_filter :admin_user,  only: :destroy 

    #force_ssl 

    def index 
    @users = User.paginate(page: params[:page]) 
    end 

    def show 
    @user = User.find(params[:id]) 
    @microposts = @user.microposts.paginate(page: params[:page]) 
    end 

    def destroy 
    @user = User.find(params[:id]) 
    if current_user?(@user) 
     redirect_to users_path, notice: "You can't destroy yourself." 
    else 
     @user.destroy 
     flash[:success] = "User destroyed." 
     redirect_to users_path 
    end 
    end 

這些在得到一個解決方案

https://gist.github.com/484787

是有幫助

http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/

+0

有一件事我剛剛發現,這不是我的問題,但究竟能* A *的問題是,讓方法是懶惰的,所以我能想象它搞砸了的預期to_not改變功能。所以我調整了使用let的代碼!當創建管理員用戶時。 –

+0

您正在測試的場景是什麼?管理員登錄,刪除鏈接隱藏,但他以某種方式工藝刪除請求? (只是問) –

+0

好問題。大部分答案是,這是Rails教程中的一個練習。事實證明這是一個很好的練習,因爲我學到了關於cookies,http請求,水豚和Rack :: Test的各種事情。我想我可能正在測試隱藏刪除鏈接的代碼失敗的場景,我希望我的控制器有備份。 –

回答

5

你混淆了RSpec的護欄要求規範其是集成測試,在模擬瀏覽器和控制器的規格,其測試控制器單獨執行。 delete(action, *args)(和get,post等) - 是一種模擬來自ActionController :: TestCase的請求的方法,因此它在您的測試中不可用。

所以你唯一的選擇是模擬瀏覽器中的點擊。我不知道你如何隱藏你的刪除鏈接,如果html在那裏但隱藏你應該能夠點擊它。如果它不存在(在生成視圖時在服務器端刪除),則可以使用capybara的page.execute_script(但您必須在此示例:js => true中啓用javascript)。您可以添加鏈接回:

page.execute_script("$('body').append("<a href="https://stackoverflow.com/users/1" data-method="delete" rel="nofollow">Destroy</a>")") 

或使AJAX調用:

page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})") 

沒有測試,但是這樣的事情應該工作。

3

試試這個:

expect { delete :destroy, :id => admin }.to_not change(User, :count) 
+0

試過了,同樣的錯誤(見上) –

+0

這對我有效。謝謝! –

6

我使用下面的解決了這個同樣的問題:

describe "should not be able to delete themselves" do 
    it { expect { delete user_path(admin) }.not_to change(User, :count) } 
end 
+1

我認爲這隻適用於控制器測試,而不是集成測試。 – Dean

+0

這個測試通過了,儘管我手動測試了它,但它不起作用。 (它應該失敗) –

6

CallumD的解決方案爲我,似乎在邁克爾·哈特爾的教程其餘推薦技術最一致的。但我想收緊的語法一點,使其與在同一教程其它規格較爲一致:

it "should not be able to delete itself" do 
    expect { delete user_path(admin) }.not_to change(User, :count) 
end 
+1

我同意。我也是這樣做的。 – KMcA

相關問題