请求规范中的存根身份验证

Posted

技术标签:

【中文标题】请求规范中的存根身份验证【英文标题】:Stubbing authentication in request spec 【发布时间】:2011-08-12 20:16:34 【问题描述】:

在编写请求规范时,如何设置会话和/或存根控制器方法? 我正在尝试在我的集成测试中排除身份验证 - rspec/requests

这是一个测试示例

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


describe "Messages" do
  include AuthenticationHelpers

  describe "GET admin/messages" do
    before(:each) do
      @current_user = Factory :super_admin
      login(@current_user)
    end

    it "displays received messages" do
      sender = Factory :jonas
      direct_message = Message.new(:sender_id => sender.id, :subject => "Message system.", :content => "content", :receiver_ids => [@current_user.id])
      direct_message.save
      get admin_messages_path
      response.body.should include(direct_message.subject) 
    end
  end
end

帮手:

module AuthenticationHelpers
  def login(user)
    session[:user_id] = user.id # session is nil
    #controller.stub!(:current_user).and_return(user) # controller is nil
  end
end

以及处理身份验证的 ApplicationController:

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user
  helper_method :logged_in?

  protected

  def current_user  
    @current_user ||= User.find(session[:user_id]) if session[:user_id]  
  end

  def logged_in?
    !current_user.nil?
  end
end

为什么无法访问这些资源?

1) Messages GET admin/messages displays received messages
     Failure/Error: login(@current_user)
     NoMethodError:
       undefined method `session' for nil:NilClass
     # ./spec/requests/authentication_helpers.rb:3:in `login'
     # ./spec/requests/message_spec.rb:15:in `block (3 levels) in <top (required)>'

【问题讨论】:

【参考方案1】:

Devise 用户注意事项...

顺便说一句,如果您使用的是Devise,@David Chelimsky 的回答可能需要稍作调整。我在集成/请求测试中所做的事情(感谢this *** post):

# file: spec/requests_helper.rb

# Rails 6
def login(user)
  post user_session_path, params: 
    user: 
      email: user.email, password: user.password
    
  
  follow_redirect!
end

# Rails 5 or older
def login(user)
  post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end

【讨论】:

当我在 rspec 模型规范中使用 'login user1' 时,我得到 #<:core:> 的未定义局部变量或方法 'user_session_path' 这假设您在 config/routes.rb 文件中有 devise_for :users。如果您指定了不同的内容,则必须相应地调整您的代码。 这对我有用,但我必须稍微修改它。我将'user[email]' =&gt; user.email 更改为'user[username]' =&gt; user.username,因为我的应用程序使用用户名作为登录名而不是电子邮件。【参考方案2】:

我发现这对 Devise 很有帮助:https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)

【讨论】:

【参考方案3】:

你也可以很容易地存根会话。

controller.session.stub(:[]).with(:user_id).and_return(<whatever user ID>)

所有 ruby​​ 特殊运算符确实是方法。调用1+11.+(1) 相同,这意味着+ 只是一个方法。同样,session[:user_id] 与在session 上调用方法[] 相同,为session.[](:user_id)

【讨论】:

这似乎是一个合理的解决方案。 这在请求规范中不起作用,而仅在控制器规范中起作用。【参考方案4】:

FWIW,在将我的 Test::Unit 测试移植到 RSpec 时,我希望能够在我的请求规范中使用多个(设计)会话登录。它需要一些挖掘,但让它为我工作。使用 Rails 3.2.13 和 RSpec 2.13.0。

# file: spec/support/devise.rb
module RequestHelpers
  def login(user)
    ActionController::IntegrationTest.new(self).open_session do |sess|
      u = users(user)

      sess.post '/users/sign_in', 
        user: 
          email: u.email,
          password: 'password'
        
      

      sess.flash[:alert].should be_nil
      sess.flash[:notice].should == 'Signed in successfully.'
      sess.response.code.should == '302'
    end
  end
end

include RequestHelpers

还有……

# spec/request/user_flows.rb
require 'spec_helper'

describe 'User flows' do
  fixtures :users

  it 'lets a user do stuff to another user' do
    karl = login :karl
    karl.get '/users'
    karl.response.code.should eq '200'

    karl.xhr :put, "/users/#users(:bob).id", id: users(:bob).id,
      "#users(:bob).id-is-funny" => 'true'

    karl.response.code.should eq '200'
    User.find(users(:bob).id).should be_funny

    bob = login :bob
    expect  bob.get '/users' .to_not raise_exception

    bob.response.code.should eq '200'
  end
end

编辑:修正错字

【讨论】:

【参考方案5】:

请求规范是对ActionDispatch::IntegrationTest 的薄包装,它不像控制器规范(包装ActionController::TestCase)那样工作。尽管有可用的会话方法,但我认为它不受支持(即它可能存在,因为其他实用程序包含的模块也包含该方法)。

我建议通过发布您用于验证用户身份的任何操作来登录。如果您为所有用户工厂设置密码“密码”(例如),那么您可以执行以下操作:

定义登录(用户) 发布 login_path, :login => user.login, :password => 'password' 结尾

【讨论】:

谢谢大卫。它工作得很好,但提出所有这些请求似乎有点矫枉过正? 如果我认为这太过分了,我不会推荐它:) 这也是最简单的可靠方式。 ActionDispatch::IntegrationTest 旨在模拟一个或多个用户通过浏览器进行交互,而无需使用真实的浏览器。在一个示例中可能有多个用户(即会话)和多个控制器,并且会话/控制器对象是最后一个请求中使用的对象。在请求之前您无权访问它们。 我必须在 Capybara 中使用page.driver.post @IanYang page.driver.post 可能是一种反模式,根据 Jonas Nicklas 在Capybara and testing APIs 中的说法)

以上是关于请求规范中的存根身份验证的主要内容,如果未能解决你的问题,请参考以下文章

Workday SOAP API:如何进行身份验证

使用 sinonjs 存根 vuex getter

如何在 PHP 中生成 QuickBlox 身份验证签名?

根据 JWT 身份验证令牌中的声明验证来自请求的 IP

如何从 ASP.NET Core 2.0 中的自定义中间件请求身份验证

雅虎身份验证中的 400 错误请求