3

我正在阅读 railstutorial.org 上的最新 Rails 教程,我被困在某个练习上(http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-users#sec:updating_deleting_exercises上的#8 )。您必须编写 rspec/capybara 测试以确保管理员无法自行删除。我有实现工作,但无法让测试正常工作。这是我的代码。我在这里发现了类似的问题:Ruby on Rails 语法https://getsatisfaction.com/railstutorial/topics/how_to_prevent_admin_user_from_deleting_themselves。但我认为这是一个较旧的教程,而不是同一个问题。

以下是 spec/requests/user_pages_spec.rb 中的相关代码:

describe "User pages" do
  subject { page }
    describe "delete links" do
      describe "as an admin user" do
        let(:admin) { FactoryGirl.create(:admin) }
        before do
          sign_in admin
          visit users_path
        end
        it "should not be able to delete themself" do
          expect { admin.delete }.should_not change(User, :count)
        end
      end
    end
  end
end

错误消息说用户数减少了 1。

为了完整起见,这是我的(工作)实现:

class UsersController < ApplicationController
  before_filter :current_admin,     only: :destroy
  def current_admin
  @user = User.find(params[:id])
    redirect_to users_path, notice: "Cannot delete current admin" if current_user?(@user)
  end
end

我哪里错了,谢谢?(我遗漏了一些方法,但希望有足够的方法来弄清楚我想要做什么)

编辑:使用 Ruby v1.9.3,Rails v3.2.3。默认情况下,管理员没有删除链接。

Edit2:这是我的工作:

规格/控制器/users_controller_spec.rb

require 'spec_helper'

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

    it "should not be able to delete themself" do
      sign_in admin
      expect { delete :destroy, :id => admin.id }.should_not change(User, :count)
    end
  end
end

users_controller.rb

def destroy
  @user = User.find(params[:id])

  if current_user?(@user)
    flash[:error] = "Cannot delete current admin"
  else
    @user.destroy
    flash[:success] = "User destroyed."
  end
  redirect_to users_path
end
4

2 回答 2

7

before_filter 的语法不正确。调用应该是这样的

before_filter :current_admin, :only => [:destroy]

您最好将这个逻辑保留在销毁操作中。由于它仅适用于该操作,因此我认为没有任何理由将其移至单独的方法/过滤器中。您指出的其他问题实际上来自本教程的旧版本,但逻辑仍然相同:

class UsersController < ApplicationController
  def destroy
    @user = User.find(params[:id])

    if current_user?(@user)
      flash[:error]  = "Cannot delete current admin"
    else
      user.destroy
      flash[:notice] = "User was successfully deleted"
    end

    redirect_to users_path
  end
end

至于您的测试,它失败了,因为您调用的是 delete 方法而不是控制器中的 destroy 操作。来自ActiveRecord::Relation

Active Record 对象没有被实例化,所以对象的回调没有被执行,包括任何 :dependent 关联选项或 Observer 方法。

由于他们要求您使用 rspec/capybara,因此您可以使用 click_link 方法来触发销毁操作。由于您在一个包含多个列表的索引页面上,因此您应该查看Capybara::Node::Finders以便可靠地选择正确的按钮。

编辑:由于您要测试控制器,而不是视图,您可以使用以下方法进行测试:

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

   it "should not be able to delete themself" do
     sign_in admin
     expect { delete :destroy, :id => admin.id }.should_not change(User, :count)
   end
 end
于 2012-06-06T03:45:54.170 回答
7

这可以在 user_pages_spec 中进行测试,这几乎是 railstutorial.org 的书似乎希望你做的。(Ruby on Rails 教程第 9 章,练习 9)。这是否是一个好主意,我留给更伟大的思想。

user_pages_spec.rb 中的测试代码如下所示:

 describe "I should not be able to delete admins" do
      before { delete user_path(admin.id) }

      it { should_not have_selector('div.alert.alert-error', text: 'Admins cannot delete themselves') }
    end

删除 user_path(admin.id) 将模拟单击“删除”并在 rspec-capybara 中工作。假设您将错误消息更改为与我的匹配,反之亦然,这将与您的控制器代码一起传递。

此外,如果只有项目,UsersController 中的 before_filter 语法似乎可以使用或不使用 []。

于 2012-11-08T15:14:29.567 回答