Rspec:在Devise :: OmniauthCallbacksController子类中测试重定向

时间:2013-05-08 20:15:59

标签: ruby-on-rails rspec devise integration-testing omniauth

Railscast on Devise and OmniAuth之后我实现了OmniauthCallbacksController < Devise::OmniauthCallbacksController,其中包含一个处理OmniAuth回调的方法:

def all
  user = User.from_omniauth(request.env["omniauth.auth"])
  if user.persisted?
    sign_in_and_redirect user
  else
    session["devise.user_attributes"] = user.attributes
    redirect_to new_user_registration_url
  end
end
alias_method :facebook, :all

routes.rb中:

devise_for :users, controllers: {omniauth_callbacks: "omniauth_callbacks", :sessions => "sessions" }

我想自定义这个,所以我试图用RSpec来测试它。问题是如何测试此方法和重定向?

如果在我放置user_omniauth_callback_path(:facebook)的规范中,它没有抱怨路线不存在,但似乎没有实际调用该方法。

根据this answer“控制器测试使用四个HTTP谓词(GET,POST,PUT,DELETE),无论您的控制器是否为RESTful。”我试过了get user_...等等,但在这里确实抱怨路线不存在。事实上,如果我rake routes它显示此路线没有HTTP动词:

user_omniauth_callback [BLANK] /users/auth/:action/callback(.:format) omniauth_callbacks#(?-mix:facebook)

你能看到我错过的东西吗?

修改

因此,在this question之后调用该方法的一种方法是:

controller.send(:all)

然而,我遇到了提问者遇到的同样错误:

ActionController::RackDelegation#content_type delegated to @_response.content_type, but @_response is nil

3 个答案:

答案 0 :(得分:5)

你需要做三件事来完成这件事。

  • 进入OmniAuth测试环境
  • 创建OmniAuth测试模拟
  • 存根你的from_omniauth方法以返回用户

这是一个可能的解决方案,在规范中输入 (例如spec / feature / login_spec.rb)。 。

let(:current_user) { FactoryGirl.create(:user) }

before do
  OmniAuth.config.test_mode = true
  OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new({
    provider: :facebook,
    uid:'12345',
    info: {
      name: "Joe"
    }
  })
  User.stub(:from_omniauth).and_return(current_user)
end

我从谷歌身份验证中对此进行了调整,因此Facebook可能需要更多字段,但这些字段只是required by omniauth docs。您应该能够通过查看数据库模式并查找与文档匹配的字段来查找正确的字段。

在我的情况下,最小值足以通过请求阶段并转移到返回我的用户的stubed out方法。

此示例还使用FactoryGirl

它可能不完美,但我希望它有所帮助。祝你好运!

-Dan

答案 1 :(得分:3)

如果您点击此处并且您正在运行 rspec 3.4 ,则此示例应该适用于您:

describe Users::OmniauthCallbacksController, type: :controller do
  let(:current_user) { FactoryGirl.create(:user) }

  before do
    OmniAuth.config.test_mode = true
    OmniAuth.config.mock_auth[:your_oauth_provider_here] = OmniAuth::AuthHash.new(
      provider: :your_oauth_provider_here,
      uid: rand(5**10),
      credentials: { token: ENV['CLIENT_ID'], secret: ENV['CLIENT_SECRET'] }
    )
    request.env['devise.mapping'] = Devise.mappings[:user]
    allow(@controller).to receive(:env) { { 'omniauth.auth' => OmniAuth.config.mock_auth[:your_oauth_provider_here] } }
    allow(User).to receive(:from_omniauth) { current_user }
  end

  describe '#your_oauth_provider_here' do
    context 'new user' do
      before { get :your_oauth_provider_here }

      it 'authenticate user' do
        expect(warden.authenticated?(:user)).to be_truthy
      end

      it 'set current_user' do
        expect(current_user).not_to be_nil
      end

      it 'redirect to root_path' do
        expect(response).to redirect_to(root_path)
      end
    end
  end
end

答案 2 :(得分:1)

我在为RSpec扭动OmniauthCallbacksController时遇到了问题,对此做了一些研究并且它对我有用。这是我的代码,如果有人发现有必要。测试是为了快乐的路径,它应该适用于RSpec eg. 3.x

的新闻版本
   require 'spec_helper'

    describe OmniauthCallbacksController, type: :controller do
      describe "#linkedin" do
        let(:current_user) { Fabricate(:user) }

        before(:each) do
          OmniAuth.config.test_mode = true
          OmniAuth.config.mock_auth[:linkedin] = OmniAuth::AuthHash.new({provider: :linkedin, uid: '12345', credentials: {token: 'linkedin-token', secret: 'linkedin-secret'}})
          request.env["devise.mapping"] = Devise.mappings[:user]

          @controller.stub!(:env).and_return({"omniauth.auth" => OmniAuth.config.mock_auth[:linkedin]})
          User.stub(:from_auth).and_return(current_user)
        end

        describe "#linkedin" do
          context "with a new linkedin user" do
            before { get :linkedin }

            it "authenticate user" do
              expect(warden.authenticated?(:user)).to be_truthy
            end

            it "set current_user" do
              expect(subject.current_user).not_to be_nil
            end

            it "redirect to root_path" do
              expect(response).to redirect_to(root_path)
            end
          end
        end

      end
    end