如何模拟RSpec的login?

我已经和Rails玩了几年了,并且制作了一些正在生产的可移动应用程序。 我一直避免做任何testing,我决定纠正这个问题。 我正在尝试编写一些应用程序的testing,这些应用程序是为已经运行但正在进行不断修订的工作而编写的。 我担心任何更改都会破坏事情,所以我想要进行一些testing并运行。 我已经阅读了RSpec的书,看了一些截屏,但是很难开始(它实际上是你实际完成之后才明白的东西)。

我试图写我的ReportsController应该是一个简单的testing。 我的应用程序的问题是,几乎整个东西都位于身份validation层后面。 如果你没有login,什么都不起作用,所以我必须模拟一个login,甚至可以发出一个简单的获取请求(虽然我想我应该写一些testing,以确保没有login的任何工作 – 我会得到以后)。

我build立了RSpec,Capybara,FactoryGirl和Guard的testing环境(不知道使用Railscasts的build议使用哪些工具)。 到目前为止我写testing的方法是像FactoryGirl那样创build一个用户,

FactoryGirl.define do sequence(:email) {|n| "user#{n}@example.com"} sequence(:login) {|n| "user#{n}"} factory :user do email {FactoryGirl.generate :email} login {FactoryGirl.generate :login} password "abc" admin false first_name "Bob" last_name "Bobson" end end 

然后像我这样写我的testing;

 require 'spec_helper' describe ReportsController do describe "GET 'index'" do it "should be successful" do user = Factory(:user) visit login_path fill_in "login", :with => user.login fill_in "password", :with => user.password click_button "Log in" get 'index' response.should be_success end end end 

这样做失败了,

  1) ReportsController GET 'index' should be successful Failure/Error: response.should be_success expected success? to return true, got false # ./spec/controllers/reports_controller_spec.rb:13:in `block (3 levels) in <top (required)>' 

有趣的是,如果我改变我的testingresponse.should be_redirect ,testing通过,这表明一切正在工作,直到那一点,但login不被识别。

所以我的问题是我该做什么来做这个login工作。 我是否需要在数据库中创build与FactoryGirl凭据相匹配的用户? 如果是这样的话,FactoryGirl在这里有什么意义(我应该甚至使用它)? 如何在testing环境中创build这个假用户? 我的authentication系统是一个非常简单的自制(根据Railscasts第250集 )。 这种login行为大概不得不复制几乎所有我的testing,所以我怎么去做一次在我的代码,并适用于任何地方?

我意识到这是一个很大的问题,所以我感谢你的看法。

答案取决于您的身份validation实施。 通常,当用户login时,您将设置一个会话variables来记住该用户,如session[:user_id] 。 您的控制器将检查before_filter的login,如果不存在此类会话variables,则redirect。 我假设你已经在做这样的事情了。

要在testing中使用此function,您必须手动将用户信息插入到会话中。 这是我们在工作中使用的一部分:

 # spec/support/spec_test_helper.rb module SpecTestHelper def login_admin login(:admin) end def login(user) user = User.where(:login => user.to_s).first if user.is_a?(Symbol) request.session[:user] = user.id end def current_user User.find(request.session[:user]) end end # spec/spec_helper.rb RSpec.configure do |config| config.include SpecTestHelper, :type => :controller end 

现在,在我们的任何控制器示例中,我们可以调用login(some_user)来模拟以该用户身份login。


我还应该提到,看起来你正在这个控制器testing中进行集成testing。 通常,您的控制器testing应该只是模拟对单个控制器操作的请求,如:

 it 'should be successful' do get :index response.should be_success end 

这专门testing一个控制器动作,这是你在一组控制器testing中想要的。 然后,您可以使用Capybara / Cucumber进行表单,视图和控制器的端到端集成testing。

spec / support / controller_helpers.rb中添加助手文件,并复制下面的内容

 module ControllerHelpers def sign_in(user) if user.nil? allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user}) allow(controller).to receive(:current_user).and_return(nil) else allow(request.env['warden']).to receive(:authenticate!).and_return(user) allow(controller).to receive(:current_user).and_return(user) end end end 

现在在spec / rails_helper.rbspec / spec_helper.rb文件中添加以下行

 require 'support/controller_helpers' RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller config.include ControllerHelpers, :type => :controller end 

现在在你的控制器规格文件。

 describe "GET #index" do before :each do @user=create(:user) sign_in @user end ... end 

devise官方链接

由于我不能使@ Brandan的答案工作,但基于它和这篇文章 ,我来到这个解决scheme:

 # spec/support/rails_helper.rb Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # Add this at top of file ... include ControllerMacros # Add at bottom of file 

 # spec/support/controller_macros.rb module ControllerMacros def login_as_admin admin = FactoryGirl.create(:user_admin) login_as(admin) end def login_as(user) request.session[:user_id] = user.id end end 

然后在你的testing中,你可以使用:

 it "works" do login_as(FactoryGirl.create(:user)) expect(request.session[:user_id]).not_to be_nil end 

用户在functiontesting中login的最简单方法是使用Warden的帮手#login_as

 login_as some_user