2011-04-26 33 views
76

在编写请求规范时,如何设置会话和/或存根控制器方法? 我想在我的集成测试存根出认证 - RSpec的/请求请求中的Stubbing验证规范

这是一个测试的例子

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 

为什么它不是possi可以访问这些资源吗?

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)>' 

回答

97

请求规格是薄的包装纸围绕ActionDispatch::IntegrationTest,它不象控制器的规格工作(其缠绕ActionController::TestCase)。尽管有一个会话方法可用,但我认为它不被支持(即可能存在,因为包含在其他实用程序中的模块也包含该方法)。

我建议通过发布登录到您用来验证用户的任何操作。如果您的密码,“密码”(例如)的所有用户的工厂,那么你可以做这样的事情:

 
def login(user) 
    post login_path, :login => user.login, :password => 'password' 
end 
+0

感谢大卫。它工作得很好,但它似乎有点矫枉过正,使所有这些要求? – 2011-04-27 11:52:52

+17

如果我认为这是过度杀伤,我不会推荐它:) – 2011-04-27 12:54:32

+4

这也是最可靠的方法。 'ActionDispatch :: IntegrationTest'旨在模拟一个或多个用户通过浏览器进行交互,而不必使用真正的浏览器。可能有多个用户(即会话)以及一个示例中的多个控制器,并且会话/控制器对象是上次请求中使用的那些对象。请求前您无权访问它们。 – 2011-04-27 13:02:45

58

顺便说一句,如果你使用的设计@大卫赫利姆斯基的答案可能需要一些调整。我在做什么在我的集成/请求测试(感谢this StackOverflow post):

# 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'时,我得到未定义的局部变量或方法'user_session_path'为# jpwynn 2012-12-20 19:36:49

+1

这假设你在'config/routes.rb'文件中有'devise_for:users'。如果你指定了不同的东西,你必须相应地调整你的代码。 – 2013-02-23 17:10:36

+0

这工作对我来说我不得不稍微修改它。因为我的应用程序使用用户名作为登录名而不是电子邮件,所以我将'user [email]'=> user.email'更改为''user [username]'=> user.username'。 – webdevguy 2016-05-24 14:25:01

2

FWIW,在移植我的Test ::单元测试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, "https://stackoverflow.com/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>) 

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

+0

这似乎是一个合理的解决方案。 – superluminary 2014-03-24 14:58:54

+1

这不适用于请求规范,但仅适用于控制器规范。 – Machisuji 2016-06-14 12:18:21