wil*_*l_d 15 rspec ruby-on-rails railstutorial.org
注意:我已经阅读了这个问题和答案,但由于某些原因,代码对我不起作用.(见下面我得到的错误)
Rails教程第9章练习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
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是在编写测试时会发送删除请求并调用destroy方法.我试过Rspec测试的解决方案,如果没有删除链接,我将在这里复制:
来自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
Run Code Online (Sandbox Code Playgroud)
但是当我运行它时,我从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)>'
Run Code Online (Sandbox Code Playgroud)
所以,我的问题是:1)为什么上面的代码失败了?2)如何模拟"删除"以便在我的控制器中调用destroy动作?
环境:Mac OSX ruby 1.9.3p194 Rails 3.2.3
宝石测试:
组:测试做宝石'rspec-rails','2.9.0'宝石'capybara','1.1.2'宝石'rb-fsevent','0.4.3.1',:require => false gem'咆哮','1.0.3'宝石'护卫','0.3.2'宝石'spork','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
Run Code Online (Sandbox Code Playgroud)
但是,我试图直接生成删除请求的任何内容都无法调用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
Run Code Online (Sandbox Code Playgroud)
并得到这个错误:
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)>'
Run Code Online (Sandbox Code Playgroud)
解
我在FOREVER之后想出来了.
我不得不使用Rack :: Test来发出DELETE请求,但是Capybara和Rack :: Test不共享相同的MockSession,所以我必须输入:remember_token和:!sample_app_session cookie并将它们放入DELETE请求中手动.这是有效的.(我在下面列出的另一个问题是,我有一个force_ssl语句,它不会让我的destroy操作被调用.
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
Run Code Online (Sandbox Code Playgroud)
在我的users_controller.rb中,我的before_filters之后有一个force_ssl语句,这样就不知何故了,所以我从来没有进行过destroy操作.
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
Run Code Online (Sandbox Code Playgroud)
这些有助于找到解决方案
https://gist.github.com/484787
http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/
小智 6
我使用以下方法解决了同样的问题:
describe "should not be able to delete themselves" do
it { expect { delete user_path(admin) }.not_to change(User, :count) }
end
Run Code Online (Sandbox Code Playgroud)
小智 6
CallumD的解决方案对我有用,并且似乎与Michael Hartl教程其余部分推荐的技术最为一致.但我想稍微收紧一下语法,使其与同一教程中的其他规范更加一致:
it "should not be able to delete itself" do
expect { delete user_path(admin) }.not_to change(User, :count)
end
Run Code Online (Sandbox Code Playgroud)
您正在混淆rspec-rails请求规范,这些规范是集成测试,并在模拟的浏览器和控制器规范中执行,它们独立地测试控制器.delete(action, *args)(和get,post等等) - 是一个模拟ActionController :: TestCase请求的方法,因此它在您的测试中不可用.
因此,您唯一的选择是在浏览器中模拟点击.我不知道你是如何隐藏你的删除链接,如果html存在但隐藏你应该能够点击它.如果它不存在(在生成视图时在服务器端删除),您可以使用capybara page.execute_script(但是您必须为此示例启用javascript :js => true).您可以添加链接:
page.execute_script("$('body').append("<a href="/users/1" data-method="delete" rel="nofollow">Destroy</a>")")
Run Code Online (Sandbox Code Playgroud)
或拨打ajax电话:
page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})")
Run Code Online (Sandbox Code Playgroud)
没有测试,但这样的事情应该工作.
这就是我最终得到的结果(Rspec 3.2):
describe 'DELETE destroy' do
before :each do
delete :destroy, { id: current_partner_role }
end
it 'destroys role' do
expect(assigns(:role).destroyed?).to be true
end
Run Code Online (Sandbox Code Playgroud)
"毁?" 方法本身是由Rails规定的,所以恕我直言,它应该可以依赖它.
| 归档时间: |
|
| 查看次数: |
18915 次 |
| 最近记录: |