6

我是 Ruby 和 Rails 的新手,这就是我阅读 Michael Hartl 的 Rails 教程的原因。我被困在第 9 章,练习 #9。我已将用户控制器中的 def destroy 代码更新为:

def destroy
  user = User.find(params[:id])
  if (current_user == user) && (current_user.admin?)
    flash[:error] = "Can not delete own admin account!"
  else
    user.destroy
    flash[:success] = "User destroyed."
  end
redirect_to users_path
end

当我通过在管理员登录时将“删除”链接添加到 current_user 来在浏览器中进行测试时,这似乎有效。但练习中说要先编写一个测试——我这样做了,但似乎没有用。这是我的测试:

describe "as admin user" do
  let(:user_admin) { FactoryGirl.create(:admin) }

  before { sign_in user_admin }

  describe "submitting a DELETE request to destroy own admin account" do
    before { delete user_path(user_admin) }
    it { should have_selector('div.alert.alert-error', text: 'delete own admin') }
  end
end

也许我正在测试的东西不应该被测试。如何测试用户控制器中对 def destroy 代码的修改?

4

7 回答 7

3

我试图让原始帖子中的代码正常工作,但没有运气。相反,我让它像这样工作(让它失败,然后通过)。这将测试正确的重定向以及正确的 Flash 消息。

测试: authentication_pages_spec.rb

  describe "as admin user" do
    let(:admin) { FactoryGirl.create(:admin) }
    before { sign_in admin }

    describe "can't delete self by submitting DELETE request to Users#destroy" do
      before { delete user_path(admin) }
      specify { response.should redirect_to(users_path), 
                  flash[:error].should =~ /Can not delete own admin account!/i }
    end
  end

实现:用户#destroy

def destroy
    user = User.find(params[:id])
    if (current_user == user) && (current_user.admin?)
      flash[:error] = "Can not delete own admin account!"
    else
      user.destroy
      flash[:success] = "User destroyed."
    end
  redirect_to users_path
  end

也许原始测试不起作用的原因是因为我们发出请求的方式?我尝试将以下各项分别添加到描述块中,但都失败了:

it { should have_selector('div.alert.alert-error', text: 'delete own admin') }

it { should have_selector('title', text: 'All users') }
it { should have_selector('h1', text: 'All users') }

因此,Capybara 似乎实际上并没有重定向到页面来检查这些选择器。我尝试了'title'和'h1',认为选择器'div.alert.alert-error'可能有问题......但是'title'和'h1'失败并出现相同的“预期CSS返回某些东西” ...

有谁知道更多关于specify { response.should ... }风格测试的工作原理?如果他们在点击控制器操作时不遵循重定向?

于 2012-04-17T00:05:39.537 回答
1

还值得指出的是,在清单 9.43 下的教程中,app/views/users/_user.html.erb 中的部分视图有一个检查,以防止在用户索引上显示当前登录的管理员用户的“删除”链接页。

因此,即使用户无法通过 Web UI 删除他们自己的帐户,我猜练习 9.9 还进一步确保了控制器级别的逻辑,以防有人为当前用户精心制作并发送了 http 删除请求。

总体而言,在 Rails 应用程序周围添加这些安全防护装置以防止出现任何奇怪的错误可能是一个很好的做法。

由于部分视图上的过滤器使您永远不会看到错误闪烁,因此您还可以通过删除“else”来简化用户控制器的销毁操作。

def destroy
  user = User.find(params[:id])
  unless current_user?(user)
    user.destroy
    flash[:success] = "User deleted."
  end
  redirect_to users_url
end
于 2014-05-09T00:10:10.077 回答
1

我也是 Rails 教程(以及一般的 Rails)的新手,遇到了同样的问题,你的问题帮助我找到了答案。

我仍然不确定您的代码为什么会失败,但以下步骤确实有效。

首先,稍微修改测试代码以使用以下结构(这里我省略了该描述块的放置位置 - 你已经找到了正确的位置):

describe "deleting herself" do
  it "should not be possible" do
    expect { delete user_path(admin) }.to_not change(User, :count).by(-1)
  end
end

请注意,我使用 expect{} 块来跟踪用户对象的数量。这肯定会导致测试变为红色(此时这很好),而检查 Flash会使测试变为红色,但检查错误 flash 似乎在这里不起作用。我真的不知道为什么!也许与发生的双重重定向有关?

接下来,编写保护代码以使测试再次变为绿色。你的代码有效(我认为),但我认为我的代码更符合习惯,因为它使用了第 9 章前面定义的会话助手。

def destroy
  user = User.find(params[:id])
  if (current_user? user) && (current_user.admin?)
    flash[:error] = "You are not allowed to delete yourself as an admin."
  else
    user.destroy
    flash[:success] = "User destroyed. ID: #{user.id}"
  end
  redirect_to users_path
end

此更改使我的测试再次变为“绿色”,从而成功完成了练习 10。

于 2012-04-10T21:03:33.927 回答
0

我也是 Rails 的新手,只是第一次做教程,你的帖子对我有很大帮助,但只是为了做出贡献,你不需要检查用户是否是破坏中的管理员,因为破坏将可用添加行时仅适用于管理员用户

before_action :admin_user,     only: :destroy

在用户控制器中。

所以只需询问是否与当前用户不同就足够了

def destroy
  usertodestroy = User.find(params[:id])
  if (current_user == usertodestroy)
    flash[:error] = 'Can´t delete own user'
  else
    usertodestroy.destroy
    flash[:success] = "User destroyed. ID: #{usertodestroy.name}"
    redirect_to users_url
  end
end

此外,测试应该只要求尝试删除后计数没有改变

describe "as admin user" do
  let(:admin) { FactoryGirl.create(:admin) }
  before { sign_in(admin) }

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

两者都不会改变结果,但它们只是让事情变得更简单。

为什么我不能真正明白为什么下一个代码在测试时无论如何都会删除用户:

describe "as admin user" do
  let(:admin) { FactoryGirl.create(:admin) }
  before { sign_in(admin) }

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

在我看来,这是直接调用 UsersController,因此不应将其删除。

于 2013-08-19T20:27:43.087 回答
0

控制器动作

def destroy
  usertodestroy = User.find(params[:id])
  if (current_user == usertodestroy)
    flash[:error] = 'Can´t delete own user'
    redirect_to root_url
  else
    usertodestroy.destroy
    flash[:success] = "User destroyed. ID: #{usertodestroy.name}"
    redirect_to users_url
  end
end

并测试

describe "as admin user" do
  let(:admin) { FactoryGirl.create(:admin) }
  before { sign_in admin, no_capybara: true }

  it "attempting to delete self" do
    expect{ delete user_path(admin) }.not_to change(User, :count)
  end
end

为我工作。

于 2015-01-15T17:08:35.793 回答
0

我永远应用了 eblume 建议,但我有一些评论和疑问:

首先,可以简化测试,因为我们不需要检查计数是否改变了一个单位,而是检查它是否改变了任何数字:

expect { delete user_path(admin) }.not_to change(User, :count)

关于控制器中的代码,也可以简化。正如我们在销毁操作之前编写了以下代码:

def admin_user
    redirect_to(root_url) unless current_user.admin?
end

如果用户是管理员,则无需在“销毁”方法中检查,它必须是管理员。

所以 if 子句变成:

if (current_user? user)

我现在的问题是:我不明白这段代码,我不知道这个检查是做什么的。

我的第一次尝试是使用以下内容:

if (current_user.id == params[:id])

但这不起作用,我不明白为什么。

于 2014-02-23T20:30:12.383 回答
0

尽管遵循了上述建议,但我的测试仍然没有通过,出现错误:

undefined method `admin?' for nil:NilClass

我认为这意味着登录存在一些问题,因为这仅作为 before_filter 'admin_user' 检查的一部分调用。

我能够通过使用非水豚版本的登录方法来解决这个问题

before { signin admin, no_capybara: true }

谢谢!

于 2013-10-10T18:39:28.607 回答