3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails6】Omniauth × devise_auth_tokenを用いたTwitterログイン機能のRequest Specを書く

Last updated at Posted at 2021-04-29

Rails × React SPAの作成に当たりOmniauth × devise_auth_tokenを用いたTwitterログイン機能を実装している。
その際のRequest Specの書き方についてメモ

どこをテストするか?

以下の記事内にいい感じの画像があったので引用。
https://qiita.com/hirakei1203/items/d7e03040a4f899d374c1
omniauth.jpeg
画像内の③から④のあたり(auth_hashを受け取ってその内容をもとにユーザーを保存する)を検証する

コード内容

実際に書いたコードがこちら

require 'rails_helper'

RSpec.describe 'OmniauthUsers', type: :request do
  before do
    OmniAuth.config.test_mode = true # これがないと実際にツイッターと通信してしまう
    OmniAuth.config.mock_auth[:twitter] = nil # テストごとに認証情報を初期化する
    Rails.application.env_config['omniauth.auth'] = twitter_mock
    Rails.application.env_config['omniauth.params'] = { 'resource_class' => 'User', 'namespace_name' => 'api_v1' } # No resource_class foundというエラーを避ける
  end

  describe 'omniauthを用いたTwitterでのログイン' do
    context 'ログインに成功' do
      it 'oauthのデータが存在する場合ログインに成功する' do
        get '/api/v1/users/twitter/callback'
        expect(response).to have_http_status(200)
      end

      it 'oauthのデータが存在する場合ユーザーモデルのカウントが1増える' do
        expect do
          get '/api/v1/users/twitter/callback'
        end.to change(User, :count).by(1)
      end

      it 'oauthのデータが存在する場合リクエストのmockのデータに応じたレスポンスが返却される' do
        get '/api/v1/users/twitter/callback'
        json = JSON.parse(response.body)
        expect(json['uid']).to eq request.env['omniauth.auth']['uid'] # 念の為一意性のカラムで検証
        expect(json['email']).to eq request.env['omniauth.auth']['info']['email'] # 念の為一意性のカラムで検証
        expect(json['provider']).to eq request.env['omniauth.auth']['provider'] # 念の為一意性のカラムで検証
      end
    end

    context 'ログイン失敗' do
      it "request.env['omniauth.auth']がnilの場合リクエストに失敗する" do
        Rails.application.env_config['omniauth.auth'] = nil
        expect do
          get '/api/v1/users/twitter/callback'
        end.to raise_error NoMethodError # undefined method [] for nil:NilClassと出る。auth_hashがnilのためにユーザー情報の取得に失敗している状態
      end
    end
  end
end

詳しく見ていこう

before do ~ end

  before do
    OmniAuth.config.test_mode = true # これがないと実際にツイッターと通信してしまう
    OmniAuth.config.mock_auth[:twitter] = nil # テストごとに認証情報を初期化する
    Rails.application.env_config['omniauth.auth'] = twitter_mock
    Rails.application.env_config['omniauth.params'] = { 'resource_class' => 'User', 'namespace_name' => 'api_v1' } # No resource_class foundというエラーを避ける
  end

コメントアウトで書いている通り。今回は3行目にあるtwitter_mockというのを用いてauth_hashを擬似的に生成する。
twitter_mockは以下のように作成

module OmniauthMocks
  def twitter_mock
    OmniAuth.config.mock_auth[:twitter] = OmniAuth::AuthHash.new({
      'provider' => 'twitter',
      'uid' => '123456',
      #  infoはTwitterのプロフィールと対応
      'info' => {
        'nickname' => 'mock_user',
        'image' => 'http://mock_image_url.com',
        'location' => '',
        'description' => 'This is a mock user.',
        'email' => 'mock@example.com',
        'urls' => {
          'Twitter' => 'https://twitter.com/MockUser1234',
          'Website' => ''
          }
        },
      'credentials' => {
        'token' => 'mock_credentails_token',
        'secret' => 'mock_credentails_secret'
        },
      'extra' => {
        'raw_info' => {
          'name' => 'Mock User',
          'id' => '123456',
          'followers_count' => 0,
          'friends_count' => 0,
          'statuses_count' => 0
          }
        }
      })
  end
end

作り方はいろいろあるだろうけど、自分の場合はsupportディレクトリ配下に配置しrails_helper.rbにて

Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
  config.include OmniauthMocks

とすることで読み込みを可能にしている。

またtwitter_mockを代入する以外にもtest_modeの有効化やmock_authをnilにする処理、Rails.application.env_config['omniauth.params'] の設定など
を行った。それについてはコメントアウトで示しているとおりです。

example

describe 'omniauthを用いたTwitterでのログイン' do
    context 'ログインに成功' do
      it 'oauthのデータが存在する場合ログインに成功する' do
        get '/api/v1/users/twitter/callback'
        expect(response).to have_http_status(200)
      end

      it 'oauthのデータが存在する場合ユーザーモデルのカウントが1増える' do
        expect do
          get '/api/v1/users/twitter/callback'
        end.to change(User, :count).by(1)
      end

      it 'oauthのデータが存在する場合リクエストのmockのデータに応じたレスポンスが返却される' do
        get '/api/v1/users/twitter/callback'
        json = JSON.parse(response.body)
        expect(json['uid']).to eq request.env['omniauth.auth']['uid'] # 念の為一意性のカラムで検証
        expect(json['email']).to eq request.env['omniauth.auth']['info']['email'] # 念の為一意性のカラムで検証
        expect(json['provider']).to eq request.env['omniauth.auth']['provider'] # 念の為一意性のカラムで検証
      end
    end

    context 'ログイン失敗' do
      it "request.env['omniauth.auth']がnilの場合ログインに失敗する" do
        Rails.application.env_config['omniauth.auth'] = nil
        expect do
          get '/api/v1/users/twitter/callback'
        end.to raise_error NoMethodError # undefined method [] for nil:NilClassと出る。auth_hashがnilのためにユーザー情報の取得に失敗している状態
      end
    end
  end
end


実際のexampleはこんな感じで基本は Rails.application.env_config['omniauth.auth'] がnilかどうかで正常形と異常形を分けている。
正常形はhttpステータス、Userのカウントの増加、レスポンスをjsonで受け取り中身がmockと一致することの3つを検証

感想

参考記事がなくて大変だったけど、どの処理をテストするのか?という点が明確であればそれなりにテスト書くこともできるな、という印象でした。いい経験でした。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?