【发布时间】:2012-06-12 01:13:29
【问题描述】:
注意:我已阅读 this 问题和答案,但由于某种原因,该代码不适用于我。 (有关我遇到的错误,请参见下文)
Rails 教程第 9 章的练习 10 要求您: 修改 [for users] 的 destroy 操作以防止管理员用户销毁自己。 (先写一个测试。)
这里棘手的部分是测试它,因为应用程序已经为当前用户隐藏了“删除”链接,所以你必须直接进行 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
我遇到的问题是为此编写将发送删除请求并调用销毁方法的测试。我尝试了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 导轨 3.2.3
用于测试的宝石:
组:测试做
宝石'rspec-rails','2.9.0'
宝石'水豚','1.1.2'
gem 'rb-fsevent', '0.4.3.1', :require => false
宝石“咆哮”,“1.0.3”
宝石 'guard-spork', '0.3.2'
宝石'spork','0.9.0'
宝石'factory_girl_rails','1.4.0'
结束
更多信息 我已经尝试了 ton 多种方法来尝试模拟单击删除链接,但似乎都不起作用。我一直在使用调试器 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)>'
解决方案
我在 FOREVER 之后才知道。
我不得不使用 Rack::Test 来发出 DELETE 请求,但是 Capybara 和 Rack::Test 不共享同一个 MockSession,所以我不得不拉入 :remember_token 和 :!sample_app_session cookie 并将它们放入手动删除请求。这是有效的。 (下面列出的另一个问题是我有一个 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
我在 users_controller.rb 中的 before_filters 之后有一个 force_ssl 语句,这不知何故把事情扔掉了,所以我从来没有去破坏行动。
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/
【问题讨论】:
-
我刚刚发现的一件事,这不是我的问题,但可能是 a 问题是 let 方法是惰性的,所以我可以想象它搞砸了期望 to_not改变功能。所以我调整了代码以使用 let!创建管理员用户时。
-
您正在测试的场景是什么?管理员登录,删除链接被隐藏,但他以某种方式制作了删除请求? (只是问)
-
好问题。大多数情况下,答案是这是 Rails 教程中的一个练习。事实证明这是一个很好的练习,因为我学到了关于 cookie、http 请求、Capybara 和 Rack::Test 的各种东西。我想我可能正在测试隐藏删除链接的代码失败并且我希望我的控制器有备份的场景。
标签: ruby-on-rails rspec railstutorial.org