Rails × React SPAの作成に当たりOmniauth × devise_auth_tokenを用いたTwitterログイン機能を実装している。
その際のRequest Specの書き方についてメモ
どこをテストするか?
以下の記事内にいい感じの画像があったので引用。
https://qiita.com/hirakei1203/items/d7e03040a4f899d374c1
画像内の③から④のあたり(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つを検証
感想
参考記事がなくて大変だったけど、どの処理をテストするのか?という点が明確であればそれなりにテスト書くこともできるな、という印象でした。いい経験でした。