【RSpec】Deviseの認証を使ったControllerのテスト

スポンサーリンク

テスト方法は GitHub の  Wiki を参考にしていますが内容は少し変更しています。

ログインメソッドの作成

まずはspec/support/controller_macros.rbにログイン用のモジュールを作成します。

login_adminのメソッドはここでは削除しました。必要なら作成してください。

user.confirm!はメソッドが既に削除されているため使用できません。代わりにconfirmメソッドを使用するか、FactoryGirl の User にconfirmed_at { Time.current }を追加します。
 ruby on rails - NoMethodError: undefined method `confirm!' for #<User - Stack Overflow

before(:each)も spec ファイル側で使いたいのでこのモジュールからは削除しています。最後に User モデルを引数で指定できるように変更しました。

module ControllerMacros
  def login_user(user=nil)
    @request.env["devise.mapping"] = Devise.mappings[:user]
    user ||= FactoryGirl.create(:user)
    sign_in user
  end
end

設定ファイルの変更

spec/rails_helper.rbspec/support/devise.rbを新しく作成して次の設定を追加します。テスト用のヘルパーと先程作ったログイン用のモジュールを読み込みます。

RSpec.configure do |config|
  config.include Devise::Test::ControllerHelpers, :type => :controller
  config.include ControllerMacros, :type => :controller
end

spec/supportディレクトリ内のファイルを読み込むようにしていなければspec/rspec_helper.rbに次の一文を追加して下さい。コメントアウトされていると思います。

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

ログイン用のモジュールを読み込む設定でextendからincludeに変えている理由は spec のbefore(:each)で呼び出したいためです。

RSpec.configure の extend 、 include はどうなっているのか - Qiita

RSpecの実行サンプル

実際に動作する spec ファイルが以下の通りです。User は引数で渡さなければ生成するようにしているため今回は省略しています。

require 'rails_helper'

RSpec.describe UsersController, type: :controller do
  describe 'GET #index' do
    context 'with assign all users' do
      before do
        login_user
        get :index
      end
      it { is_expected.to have_http_status :success }
      it { is_expected.to render_template :index }
      end
  end
end

実行結果です。

$ ./bin/rspec spec/controllers/users_controller_spec.rb 

UsersController
  GET #index
    with assign all users
      should respond with 200
      should render template index

Finished in 0.70331 seconds (files took 3.8 seconds to load)
2 examples, 0 failures

current_userのモックを作る

今回の認証を通してからのテストとは別の話しですが、Device に含まれるcurrent_userメソッドのモックを作成して指定したユーザーを返すようにするには次のようにします。

let(:user) { FactoryGirl.create(:user) }
before do
  allow(controller).to receive(:current_user).and_return(user)
end