#はじめに
前の記事の続きです。
Twitter認証をDeviseなしで実装した後、
結合テストコードを記述した時のメモ書きです。
備忘録も兼ねて記事にしたいと思います。
【実行環境】
・ Rails 6.0.0
・ Ruby 2.6.5
・ RSpec 4.0.0
【使用gem】
gem 'omniauth'
gem 'omniauth-twitter'
【参考にさせていただいた記事】
https://qiita.com/ngron/items/88f04a2d864f9f33b389
https://www.wantedly.com/companies/clueit/post_articles/43708
詳細な説明はあまり載せていないので、詳しく知りたい場合は参考にさせていただいた記事を読んでいただければと思います。
#外部APIを使用したテストの場合
今回Twitterログインの部分をテストするにあたって問題となるのは外部APIであるTwitterAPIを使用していることにあります。
今回行いたいテストは「ログインしようとしているユーザーがTwitterのアカウント認証を許可したかどうか、によって変化したその後のログイン状況を確認する」ということです。
このテストを行うには、実際にTwitterAPIよりアカウント情報を取得しなくてはいけません。
しかしテストで外部APIを利用することには、幾つかのデメリットがあります。
テストにもかかわらず実際に投稿をしてしまったり、外部APIの使用に制限などがあったり、外部API周辺でのエラーがあったりです。
ということで「モック」を利用してダミーのユーザーデータを作成します。
https://qiita.com/jnchito/items/640f17e124ab263a54dd
モックについてはこちらが詳しいです。
#モック(テストで使うダミーデータ)の作成
ユーザーデータのハッシュが入ってるのが、request.env["omniauth.auth"]なので、
その部分にいれるモック(ダミーユーザーデータ)を擬似的に作ってテストをしていきます。
今回はサポートモジュールを用いて記述する形にします。
spec/support/omniauth_mocks.rbに書いて最後にincludeするようにします。
下記では認証に成功した時用のものと、認証に失敗した(コールバックに失敗した)時用の2種類を作成しています。
module OmniauthMocks
def twitter_mock
OmniAuth.config.mock_auth[:twitter] = OmniAuth::AuthHash.new({
"provider" => "twitter",
"uid" => "123456",
"info" => {
"name" => "Mock User",
"image" => "http://mock_image_url.com",
"location" => "",
"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
def twitter_invalid_mock
OmniAuth.config.mock_auth[:twitter] = :invalid_credentails
end
end
#RSpecの設定
~省略~
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } #サポートモジュールを使う場合はこの部分のコメントアウトを外しておく
~省略~
# OmniAuthをテストモードに変更
OmniAuth.config.test_mode = true
RSpec.configure do |config|
~省略~
# サポートモジュールの読み込み(テストで使う仮のユーザーデータ)
config.include OmniauthMocks
end
#ルーティング
ルーティングはこのように設定してあります
get '/auth/:provider/callback', to: 'sessions#create' #ログイン認証
get '/logout', to: 'sessions#destroy' #ログアウト用
get "/auth/failure", to: "sessions#failure" #認証失敗時用
#テストコードを書くためのファイルを用意する
% rails g rspec:system users
#実際に記述したテストコード
テストの前にOmniAuth.config.mock_auth[:twitter]
を初期化し、twitter_mock
あるいはtwitter_invalid_mock
をセットします。
require 'rails_helper'
RSpec.describe 'Users', type: :system do
before do
@user = FactoryBot.create(:user)
OmniAuth.config.mock_auth[:twitter] = nil
end
context 'Twitter認証ができるとき' do
it 'ログインボタンを押してユーザーがTwitter認証を許可した時' do
Rails.application.env_config['omniauth.auth'] = twitter_mock
visit root_path
expect(page).to_not have_content('マイページ') # ログイン前はマイページという表示が無い
find_link('ログイン', href: '/auth/twitter').click # ログインボタンをクリックしてTwitter認証を行う
expect(page).to have_content('マイページ') # リダイレクトされてTOPに戻るとログインできている
end
end
context 'Twitter認証ができないとき' do
it 'ログインボタンを押してユーザーがTwitter認証をキャンセルした時' do
Rails.application.env_config['omniauth.auth'] = twitter_invalid_mock
visit root_path
expect(page).to_not have_content('マイページ') # ログイン前はマイページという表示が無い
find_link('ログイン', href: '/auth/twitter').click # ログインボタンをクリックしてTwitter認証を行う
expect(page).to_not have_content('マイページ') # リダイレクトされてTOPに戻るとログインできてない
end
end
end
#FactoryBotの中身
これ自体にあまり意味はないので無理にfactorybotを使う必要はありません。
FactoryBot.define do
factory :user do
uid { 123456 }
nickname { Faker::Name.first_name}
name {Faker::Name.initials(number: 2)}
image {Faker::Lorem.sentence}
end
end
#テストコードの実行
% bundle exec rspec spec/system/users_spec.rb
#おわりに
メモ書き程度ですが、何かの一例になれば幸いです。
ついでに改善した方が良い部分などあれば指摘いただけると助かります。