如何测试权限管理的Rails应用程序?

时间:2010-08-12 21:40:25

标签: ruby-on-rails

我正在开发一个Rails应用程序,它要求您登录以查看几乎任何内容。对于单元测试,这不是问题,因为它们直接与模型交互。对于集成测试,这不是一个大问题,因为我可以通过Rails的post方法获得适当的用户登录。将所有权限敏感的测试放在集成测试中似乎是有意义的(例如,测试用户无法编辑其他人的评论)。

但是,在处理功能测试时遇到了问题。对我来说,测试与用户状态无关的功能(除了作为网关的能力之外)与用户权限无关,这似乎是有道理的。现在,我无法找到一个好方法,因为所有请求都会将测试客户端退回到登录页面。

我尝试的一个不优雅的解决方案是在setup方法中将此日志放入请求中,但Rails测试程序尝试访问操作而不是路径。特别是,我有

def setup
  post /sessions/create, {:username => 'testdbadmin', :password => 'password}
end

但根据日志,它只是试图访问SomeController#/ sessions / create,这显然不是意图。

我觉得我错过了一些大的,内置的方法来做到这一点。请指教。

2 个答案:

答案 0 :(得分:4)

我在功能测试中通过在我的设置中设置有效的登录会话数据来做到这一点,有效地模拟用户已经过身份验证的请求(或者根本没有,视情况而定)。

我的test_helper.rb中有一个帮助方法:

def login(typ = :user, options = {})
    user = Factory.create(typ, {:username => "Joe"}.merge(options))
    session["user_id"] = user.id.to_s
  end

然后在我的功能测试中:

class AccountsControllerTest < ActionController::TestCase
  context "Fresh session" do
    setup do
      @user = Factory.create(:user)
    end

    should "be redirected to the login page if not logged in" do
      get :show
      assert_redirected_to login_path
    end

  context "Logged in user with profile" do
    setup do
      @user = login
    end

    should "get the account home page" do
      get :show
      assert_response :success
    end
  end
end

答案 1 :(得分:1)

我不会在功能测试中测试所有这些,而是​​组合使用功能测试和Cucumber功能。您希望在用户登录时对其进行测试,然后他们可以访问应用程序的各个部分。这是黄瓜特色。然后,您希望在用户未登录时测试它们无法访问这些部分。那是功能测试。

我会像这样编写Cucumber功能:

Feature: Viewing accounts
  In order to update my account information
  As a user
  I want to access my profile page

  Background:
    Given a user exists:
      | login | password      |
      | Radar | superpassword |
    And I am logged in as them

  Scenario: My profile page
    When I follow "Account"
    Then I should see "Profile Information"

我在这里假设了几件事。首先,您的用户使用登录名和密码登录。您可能将此作为电子邮件和密码,但我确信您理解其中的要点。

“我以他们身份登录”步骤实际上并未设置会话,而是实际上经历了登录用户的过程。我会像这样编写这两个后台步骤*:

Given /^a user exists:$/ do |table|
  table.hashes.each do |user|
    @user = User.create!(user)
  end
end


Given /^I am logged in as them$/ do 
  steps(%Q{
    Given I am on the homepage
    And I follow "Login"
    And I fill in "Login" with "#{@user.login}"
    And I fill in "Password" with "#{@user.password}"
    And I press "Login"
    Then I should see "Login successful."
  })
end

定义了这些步骤后,将运行Scenario。我假设您在页面的某个位置有一个链接,登录操作重定向到“帐户”。单击此链接后,我还假设它转到显示“个人资料信息”的页面。

*这是一个有点人为的例子。 “用户存在”步骤可以占用多行。


现在进行功能测试。我是一个RSpec的人,所以我会这样写。

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe AccountsController do

  context "not logged in users" do
    it "cannot access the profile action" do
      get :profile
      flash[:alert].should eql("You must be logged in to access that action.")
      response.should redirect_to(login_path)
    end
  end
end

检查用户是否已登录的before_filter将通过此功能测试进行测试。因为它是private方法(它是private方法,对吗?)不应该直接测试它。我再次做出一些假设,当before_filter“失败”时,它会为某个特定消息设置flash[:alert]并重定向到login_path


总而言之:通过使用Cucumber点击并填写网站上的表单来测试用户可以做的所有事情,但是对于诸如转到受保护的操作或缺少资源之类的事情,请使用功能测试/控制器规范。