requests spec・コントローラーのテストについての話
モデルと結合テストは問題なく終わり、コントローラーのテストについてあれこれ調べていたがなかなか自分に理解できるものがなく、困っていました。
公開されているGitHubのコードを見てもコントローラーのテストコードをやっている人が少なかったというのも一つの要因なのですが、試行錯誤してなんとか完成しました。
正直これでいいのかちょっとわからない部分もあるので、もし知見がある方がこの記事を見て下さったらコメント頂けると嬉しいです。
adminユーザーの生成で困ったこと
DBにadminカラムを作ってtrue
かfalse
かで判別しています。
FactoryBotでユーザーを生成するために書いたコードが下記のものです。
FactoryBot.define do
factory :user do
nickname { '野比のび太' }
sequence(:email) { |n| "tester#{n}@example.com" }
password { 'q11111' }
password_confirmation { password }
profile { 'なんとかしてよドラえもん' }
admin { false } ⬅️一般ユーザー
end
factory :admin_user do
nickname { 'ドラえもん' }
sequence(:email) { |n| "tester#{n}@example.com" }
password { 'a11111' }
password_confirmation { password }
profile { 'いつまでも子供じゃないんだよしっかりしろよ' }
admin { true } ⬅️adminユーザー
end
end
そしてusers_spec.rbには以下のように記述。
RSpec.describe UsersController, type: :request do
let(:user) { FactoryBot.create(:user) }
let(:admin_user) { FactoryBot.create(:user, admin: true) }
#let(:admin_user) { FactoryBot.create(:admin_user) }⬅️この書き方でもダメでした
これでadminユーザーが作成されるはずだと思っていたのですがbinding.pry
で止めて中身を見るとadmin_userが作成されていませんでした。
色々書き方を変えてやってみたところ、let
を使わずにbefore do
にしたらうまくいきました。
RSpec.describe UsersController, type: :request do
before do
@user = FactoryBot.create(:user)
@admin_user = FactoryBot.create(:user, admin: true)
#@admin_user = FactoryBot.create(:admin_user)⬅️この書き方でもダメでした
end
ひとまずこれで一般ユーザーとadminユーザーを分けることができるようになりました。
editアクションのテストで困ったこと
コントローラーのテストをする上でまず困ったのがeditアクションのテストでした。
authenticate_user!
を使用しているためコントローラーのテスト実行時に、
「ログインしていないユーザー」とみなされてしまってリダイレクトされてしまうのです。
リクエストが正常のレスポンスではなくなってしまうと、テスト項目の
expect(response.status).to eq 200
ここの200が302になってしまいます。
また、編集できる時とできない時、両方のテストをしなければいけないため、コントローラーのテストコード上でログインをさせることができれば正常な返り値が得られると考えました。
deviseのメソッドをテストコード上でも使えるようにするには
テストコード上でログインさせるメソッドとして結合テストコード時に使っていたmoduleでやってみたところ、うまくいきませんでした。
調べた結果、コントローラーのテストではdeviseのメソッドを使用できるとのことだったのでそちらに切り替えました。
RSpec.configure do |config|
#deviseのsign_inメソッドが使えるようになる
config.include Devise::Test::IntegrationHelpers, type: :request
end
これでsign_in
というメソッドが使用できるようになりました。
自分の場合は@user
と定義しているのでそのまま使います。
require 'rails_helper'
RSpec.describe UsersController, type: :request do
before do
@user = FactoryBot.create(:user)
end
describe 'GET #edit' do
context 'userがログインしているとき' do
before do
sign_in @user ⬅️ここでログイン
end
it 'editアクションにリクエストすると正常にレスポンスが返ってくる' do
get edit_user_path(@user)
expect(response.status).to eq 200 ⬅️編集画面に遷移できている
end
it "editアクションにリクエストするとレスポンスに登録済みuserの名前が存在する" do
get edit_user_path(@user)
expect(response.body).to include @user.nickname
end
it "editアクションにリクエストするとレスポンスに登録済みuserのプロフィールが存在する" do
get edit_user_path(@user)
expect(response.body).to include @user.profile
end
end
context 'userがログインしていないとき' do
it 'editアクションにリクエストすると正常にレスポンスが返ってくる' do
get edit_user_path(@user)
expect(response.status).to eq 302 ⬅️トップページにリダイレクトしている
end
end
end
end
これでうまくいきました。
admin_userにしたいときはbefore doで@admin_user
を定義して、
sign_in @admin_user
とすることで他のコントローラーでも問題なく処理ができました。
テストコードをやるとアプリケーションの仕様がよく理解できると言いますが、理由がよくわかりました。
細かいところに気がついたり、仕様そのものを一から見直す結果になって理解が深まったと実感できました。
参考にさせていただきました
@iwkmsy9618様
[Rails]Request Specでのログインの実施
ありがとうございました。