ruby-on-rails – RSpec测试破坏方法(Rails Tutorial 3.2 Ch.9,Ex.10)

注意:我已经阅读了 this的问题和答案,但由于某些原因代码并不适合我. (见下面的错误我得到)

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

我遇到的问题是编写这个测试,发送删除请求并调用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,: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
ruby1.9.3p194
Rails 3.2.3

宝石测试:
组:测试
宝石’rspec-rails’,’2.9.0′
宝石’capybara’,’1.1.2′
gem’rb-fsevent’,’0.4.3.1′,require =>假
宝石’咆哮’,’1.0.3′
宝石’护卫’,’0.3.2′
宝石’,’0.9.0′
gem’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),: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,: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放入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),#{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,: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/

解决方法

您将混淆rspec-rails请求规范,这些规范是集成测试,并在模拟浏览器中执行,控制器分别指定哪个测试控制器. delete(action,* args)(以及获取,发布等) – 是一种模拟来自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>")")

或进行ajax调用

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

没有测试,但这样的东西应该工作.

相关文章

validates:conclusion,:presence=>true,:inclusion=>{...
一、redis集群搭建redis3.0以前,提供了Sentinel工具来监控各...
分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣...
上一篇博文 ruby传参之引用类型 里边定义了一个方法名 mo...
一编程与编程语言 什么是编程语言? 能够被计算机所识别的表...
Ruby类和对象Ruby是一种完美的面向对象编程语言。面向对象编...