top_logo

Auth0のJWT認証をRspecのスタブを使ってテスト

2022年 05月 04日

Rspec で JWT 認証をする場合、テストをパスするには実際にユーザーが使っている JWT トークンをセットしなければなりません。


しかしユーザーの認証トークンを使っていて尚且つ、特定の値に依存するようなテストを書くのは望ましくないです。


今回は Rspec のスタブを使ってそれを回避します。


コントローラーの JWT 認証

コントローラーでは以下のような認証コードを書いています。

app/controllers/secured_controller.rb
class SecuredController < ApplicationController
  before_action :authorize_request

  private

  def authorize_request
    authorization = Authorization::AuthorizationService.new(request.headers)
    @current_user = authorization.current_user
  rescue JWT::VerificationError, JWT::DecodeError
    render json: { errors: ['Not Authenticated'] }, status: :unauthorized
  end

  attr_reader :current_user
end

ログインユーザーしか実行できないアクションではbefore_action :authorize_requestによってアクションの実行前にauthorize_requestが実行される仕組みです。


そして以下の部分で、ログインユーザーを呼び出すための認証を行なっているのですが Rspec を書く際、存在するトークンを使わずにどうやってテストをパスするか考えなくてはいけません。


  @current_user = authorization.current_user

スタブを使う

上記の問題を Rspec のスタブを使って解決します。


current_userメソッドが実行された際に、Rspec 側で用意したcurrent_userを返すスタブを作成します。


以下のモジュールを作成して読み込みます

spec/support/authorization_helper.rb
module AuthorizationHelper
  def authorization_stub
    allow_any_instance_of(SecuredController).to receive(:authorize_request).and_return(current_user)
    allow_any_instance_of(SecuredController).to receive(:current_user).and_return(current_user)
  end
end

spec/rails_helper.rb
RSpec.configure do |config|
  config.include AuthorizationHelper
.
.
end

authorize_requestメソッドとcurrent_userメソッドが呼び出された時、それぞれに対して rsepc 側で作成したcurrent_userを返すようにしました。


テスト例

テストの一例は以下のようになります。


spec/requests/api/v1/user/recommended_members_spec.rb
RSpec.describe '推しメン登録機能 Api::V1::Users::RecommendedMembers', type: :request do
  let!(:current_user) { create(:user) }


  before do
    # authorize_requestメソッドが呼ばれたらlet!(:current_user)を返す。
    # SecuredControllerのcurrent_userメソッドが呼ばれたらlet!(:current_user)を返す。
    authorization_stub
  end

  describe 'ユーザーが推しメンを閲覧 GET api/v1/user/recommended_members' do
    let(:recommended_member_num) { 5 }
    let(:http_request) { get api_v1_user_recommended_members_path}

    before do
      create_list(:recommended_member, recommended_member_num, user: current_user)
    end

    context '正常系' do
      it '推しメンを閲覧できること' do
        http_request
        expect(body['data'].count).to eq(recommended_member_num)
        expect(response).to be_successful
        expect(response).to have_http_status(:ok)
      end
    end
  end
end

以下のコードによって先ほどのスタブを呼び出しています。


  before do
    # authorize_requestメソッドが呼ばれたらlet!(:current_user)を返す。
    # SecuredControllerのcurrent_userメソッドが呼ばれたらlet!(:current_user)を返す。
    authorization_stub
  end

そしてhttp_requestを実行しauthorize_requestメソッドとcurrent_userメソッドが呼び出された時、それぞれに対して rsepc 側で作成したcurrent_userを返しています。


let!(:current_user) { create(:user) }
.
.
.
let(:http_request) { get api_v1_user_recommended_members_path }
.
.
.
http_request

解説は以上になります。


ここまで、読んでいただきありがとうございました!


分かりにくい、または間違っているところあれば連絡いただければと思います。

連絡先

何か連絡をしたい際は以下 SNS、メールでお願いいたします。

Twitter:  https://twitter.com/naka_ryo_z

Github:  https://github.com/ryotaro-tenya0727

メール:   ryotaro123110@gmail.com

見出しへのリンク

© 2024, written by Nakayama

Powered by Gatsby