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


84

在编写请求规范时,如何设置会话和/或存根控制器方法?我正在尝试在集成测试中保留身份验证-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)>'

Answers:


101

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

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

def登录(用户)
  发布login_path,:login => user.login,:password =>'password'
结束

1
谢谢大卫。效果很好,但是发出所有这些请求似乎有点过头了?
乔纳斯·尼尔森

19
如果我认为这是过分的
杀手

6
这也是可靠地执行此操作的最简单方法。ActionDispatch::IntegrationTest旨在模拟一个或多个通过浏览器进行交互的用户,而无需使用实际的浏览器。一个示例中可能有一个以上的用户(即会话)和一个以上的控制器,并且会话/控制器对象是上一个请求中使用的对象。在请求之前,您无权访问它们。
David Chelimsky

17
我必须使用page.driver.post与水豚
杨叙

page.driver.post根据Capybara中的Jonas Nicklas的说法,@ IanYang可能是反模式,并且正在测试API
Epigene

61

给Devise用户的提示...

顺便说一句,如果您使用的是Devise,@ David Chelimsky的答案可能需要稍作调整。我在集成/请求测试中所做的事情(由于此StackOverflow帖子):

# file: spec/requests_helper.rb
def login(user)
  post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end

2
当我在rspec模型规范中使用“ login user1”时,对于#<RSpec :: Core:
jpw 2012年

1
这假定您已devise_for :usersconfig/routes.rb文件中。如果您指定了其他内容,则必须相应地调整代码。
fearless_fool13年

这对我有用,但我必须对其稍加修改。我更改'user[email]' => user.email为,'user[username]' => user.username因为我的应用使用用户名而不是电子邮件作为登录名。
webdevguy

3

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

编辑:固定错别字


-1

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

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

所有红宝石特殊运算符的确是方法。调用1+1与相同1.+(1),这意味着+仅是一种方法。类似地,session[:user_id]是与调用方法[]session,作为session.[](:user_id)


这似乎是一个合理的解决方案。
superluminary 2014年

2
这不适用于请求规范,而仅适用于控制器规范。
Machisuji

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.