Rails/Rspec 使用基本身份验证使测试通过

76
这是我的应用控制器文件(application_controller.rb)中的HTTP基本身份验证。
before_filter :authenticate

protected

def authenticate
  authenticate_or_request_with_http_basic do |username, password|
    username == "username" && password == "password"  
  end
end

和我的主页控制器的索引操作的默认测试(spec/controllers/home_controller_spec.rb)

require 'spec_helper'

describe HomeController do

describe "GET 'index'" do
  it "should be successful" do
    get 'index'
    response.should be_success
  end
end

测试无法运行是因为身份验证方法的问题。我可以注释掉"before_filter :authenticate"来运行它们,但我想知道是否有办法让它们与该方法一起工作。
谢谢!

截至Rails 6,看起来这个答案是有效的。 - Jason Swett
7个回答

145

更新(2013年):Matt Connolly提供了一个GIST,也适用于请求和控制器规范:http://gist.github.com/4158961


如果你有很多测试要运行,并且不想每次都包含它(DRYer code),还有另一种方法:

创建/spec/support/auth_helper.rb文件:

module AuthHelper
  def http_login
    user = 'username'
    pw = 'password'
    request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
  end  
end

在你的测试规范文件中:

describe HomeController do
  render_views

  # login to http basic auth
  include AuthHelper
  before(:each) do
    http_login
  end

  describe "GET 'index'" do
    it "should be successful" do
      get 'index'
      response.should be_success
    end
  end

end

来源在这里 - 归档站点


1
它显示请求为空。有什么办法可以解决吗? - chourobin
6
在请求规格中,您可以有多个请求,因此request为空。相反,您需要创建一个 env 哈希 env = {},在您的 http_login 方法中更新它,然后将 env 显式地传递进去,如 get '/', {}, env - Jonathan Tran
6
对于请求和控制器规范,以上内容需要进一步扩展:https://gist.github.com/4158961 - Matt Connolly
1
当我尝试使用以下代码时,出现“未定义方法'env' for nil:NilClass”错误:@request.env['HTTP_AUTHORIZATION'] = ActionController :: HttpAuthentication :: Token.encode_credentials(“test_access1”)。为什么会这样? - deepwinter
这是对此的一个更方便的处理方式:https://gist.github.com/lehresman/794f261708c82962763f - Luke Ehresman
显示剩余2条评论

20
抱歉,我没有搜索得够多,解决方案似乎是这样的:
describe "GET 'index'" do
  it "should be successful" do
    @request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("username:password")
    get 'index'
    response.should be_success
  end
end

8
一些答案建议设置request.env,但这是不安全的,因为请求可能为空,并且你最终会得到private method env' called for nil:NilClass的错误,特别是在使用rspec -e运行单个测试时。
正确的方法是:
def http_login
  user = 'user'
  password = 'passw'
  {
    HTTP_AUTHORIZATION: ActionController::HttpAuthentication::Basic.encode_credentials(user,password)
  }
end

get 'index', nil, http_login

post 'index', {data: 'post-data'}, http_login

谢谢,这是唯一一种在我的系统测试中使用Capybara有效的方法。 - Simon Jakobi

7

对于我来说,使用Rails 6时,我需要在rspec get方法中使用关键字参数,如:get route, params: params, headers: headers

验证助手方法

module AuthHelper
  def headers(options = {})
    user = ENV['BASIC_AUTH_USER']
    pw = ENV['BASIC_AUTH_PASSWORD']

    { HTTP_AUTHORIZATION: ActionController::HttpAuthentication::Basic.encode_credentials(user,pw) }
  end
  def auth_get(route, params = {})
    get route, params: params, headers: headers
  end
end

以及rspec请求测试。

describe HomeController, type: :request do    
  include AuthHelper

  describe "GET 'index'" do
    it "should be successful" do
      auth_get 'index'
      expect(response).to be_successful
    end
  end

end

4
使用Rspec测试Grape APIs时,以下语法可行。
        post :create, {:entry => valid_attributes}, valid_session

valid_session是什么

{'HTTP_AUTHORIZATION' => credentials}

并且

credentials = ActionController::HttpAuthentication::Token.encode_credentials("test_access1")

4

这些是控制器和请求规范的绝佳解决方案。

对于使用Capybara进行功能测试,以下是使HTTP基本身份验证正常工作的解决方案:

spec/support/when_authenticated.rb

RSpec.shared_context 'When authenticated' do
  background do
    authenticate
  end

  def authenticate
    if page.driver.browser.respond_to?(:authorize)
      # When headless
      page.driver.browser.authorize(username, password)
    else
      # When javascript test
      visit "http://#{username}:#{password}@#{host}:#{port}/"     
     end
  end

  def username
    # Your value here. Replace with string or config location
    Rails.application.secrets.http_auth_username
  end

  def password
    # Your value here. Replace with string or config location
    Rails.application.secrets.http_auth_password
  end

  def host
    Capybara.current_session.server.host
  end

  def port
    Capybara.current_session.server.port
  end
end

然后,在你的规范中:

feature 'User does something' do
  include_context 'When authenticated'

  # test examples
end

非常有帮助! :) - user5853457
无法在Chrome、IE和Safari(至少)中使用(您不能再使用URL登录http auth了)。请参考:https://medium.com/@lmakarov/say-goodbye-to-urls-with-embedded-credentials-b051f6c7b6a3 - scoudert

0

我的解决方案:

stub_request(method, url).with(
  headers: { 'Authorization' => /Basic */ }
).to_return(
  status: status, body: 'stubbed response', headers: {}
)

使用gem webmock
你可以通过更改来加强验证:
/Basic */ -> "Basic #{Base64.strict_encode64([user,pass].join(':')).chomp}"

URL - 可以是正则表达式


1
我认为在最后的代码片段中不需要使用chomp,因为strict_encode64不会像文档中所说的那样添加换行符。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接