如何测试after_sign_in_path_for(资源)?

我已经在我的Rails应用程序上设置了身份validation和注册设置。 我正在使用after_sign_in_path_for()来根据各种场景在用户after_sign_in_path_for()时自定义重定向。

我问的是如何测试这种方法? 它很难被隔离,因为当用户进入时,Devise会自动调用它。我想做这样的事情:

 describe ApplicationController do describe "after_sign_in_path_for" do before :each do @user = Factory :user @listing = Factory :listing sign_in @user end describe "with listing_id on the session" do before :each do session[:listing_id] = @listing.id end describe "and a user in one team" do it "should save the listing from the session" do expect { ApplicationController.new.after_sign_in_path_for(@user) }.to change(ListingStore, :count).by(1) end it "should return the path to the users team page" do ApplicationController.new.after_sign_in_path_for(@user).should eq team_path(@user.team) end end end end end 

但这显然不是这样做的原因,因为我只是得到一个错误:

  Failure/Error: ApplicationController.new.after_sign_in_path_for(@user) RuntimeError: ActionController::Metal#session delegated to @_request.session, but @_request is nil: #"text/html"}, @_status=200, @_request=nil, @_response=nil> 

那么,我该如何测试这种方法呢?

奇怪的是,我今天想知道这件事。 这就是我想出的。 我创建了一个ApplicationController的匿名子类。 在这个匿名子类中,我公开了我想要测试的受保护方法作为公共方法。 然后我直接测试了它们。

 describe ApplicationController do controller do def after_sign_in_path_for(resource) super resource end end before (:each) do @user = FactoryGirl.create(:user) end describe "After sigin-in" do it "redirects to the /jobs page" do controller.after_sign_in_path_for(@user).should == jobs_path end end end 

在类似的说明中 – 如果您想在注册后测试重定向,则有两个选项。

首先,您可以遵循类似于上面的模式,并在RegistrationsController中非常直接地测试该方法:

 require 'spec_helper' describe RegistrationsController do controller(RegistrationsController) do def after_sign_up_path_for(resource) super resource end end describe "After sign-up" do it "redirects to the /organizations/new page" do @user = FactoryGirl.build(:user) controller.after_sign_up_path_for(@user).should == new_organization_path end end end 

或者,您可以采用更集成测试的方法并执行以下操作:

 require 'spec_helper' describe RegistrationsController do describe "After successfully completing the sign-up form" do before do @request.env["devise.mapping"] = Devise.mappings[:user] end it "redirects to the new organization page" do post :create, :user => {"name" => "Test User", "email" => "test@example.com", "password" => "please"} response.should redirect_to(new_organization_path) end end end 
 context "without previous page" do before do Factory.create(:user, email: "junior@example.com", password: "123456", password_confirmation: "123456") request.env["devise.mapping"] = Devise.mappings[:user] post :create, user: { email: "junior@example.com", password: "123456" } end end it { response.should redirect_to(root_path) } context "with previous page" do before do Factory.create(:user, email: "junior@example.com", password: "123456", password_confirmation: "123456") request.env["devise.mapping"] = Devise.mappings[:user] request.env['HTTP_REFERER'] = 'http://test.com/restaurants' post :create, user: { email: "junior@example.com", password: "123456" } end it { response.should redirect_to("http://test.com/restaurants") } end 

对于新手,我建议这样做:

 RSpec.describe ApplicationController, type: :controller do let(:user) { create :user } describe "After sing-in" do it "redirects to the /yourpath/ home page" do expect(subject.after_sign_in_path_for(user)).to eq(yourpath_root_path) end end end 

我最近通过Google找到了这个答案,并认为我会添加我的解决方案。 我不喜欢接受的答案,因为它正在测试应用程序控制器上的方法的返回值,并测试应用程序的所需行为。

我最后只是测试调用以创建一个新的会话作为请求规范。

 RSpec.describe "Sessions", type: :request do it "redirects to the internal home page" do user = FactoryBot.create(:user, password: 'password 123', password_confirmation: 'password 123') post user_session_path, params: {user: {email: user.email, password: 'password 123'}} expect(response).to redirect_to(internal_home_index_path) end end 

(Rails 5,Devise 4,RSpec 3)