【各章まとめ】Everyday Rails - RSpecによるRailsテスト入門 - 第5章 コントローラスペック
自分の備忘用に記載。
そのため書籍から内容の増減があります。
本章の前提
- コントローラのテストは削除、あるいは統合テスト等への移行が推奨
- そのため、既存システムでコントローラのテストがある場合に備えて学ぶ
- 筆者は、コントローラのテストはアクセス制御が正しく機能しているか確認するテストに限定
コントローラスペックの基本
スペック作成
spec/controllers/home_controller_spec.rbが作成される
bin/rails g rspec:controller home
正しいレスポンスが戻ってくることをテスト
describe "#index" do it "responds successfully" do get :index expect(response).to be_success end it "responds a 200 response" do get :index expect(response).to have_http_status "200" end end
ログイン状態をシュミレートしてテストする(Device Gemを用いる場合)
Deviceのテストヘルパーを利用する
spec/rails_helper.rbに追加
config.include Devise::Test::ControllerHelpers, type: :controller
ログインしてからテスト実行
sign_inメソッドでログインできる
spec/controllers/projects_controller_spec.rb
describe "#index" do let(:user) { FactoryBot.create(:user) } it "responds successfully" do sign_in user get :index expect(response).to be_success end it "returns a 200 response" do sign_in user get :index expect(response).to have_http_status "200" end end
ログインせずにアクションを呼び出して失敗することもテスト
- 302を返す
- ログインページにリダイレクトすること
を確認
spec/controllers/projects_controller_spec.rb
describe "#index" do context "as an authenticated user" do (省略) end context "as a guest" do it "responds successfully" do get :index expect(response).to have_http_status "302" end it "redirect to the sign-in page" do get :index expect(response).to redirect_to "/users/sign_in" end end end
コントローラのアクションにparams / パラメータを渡す
ユーザーが自分のプロジェクトを表示できることを確認するテストと、
ユーザーが他ユーザーのプロジェクトを表示できないことを確認するテスト
get :show, params: { id: @project.id }の箇所でparamsを渡している
describe "#show" do context "as an authenticated user" do let(:user) { FactoryBot.create(:user) } let(:project) { FactoryBot.create(:project, owner: user) } it "responds successfully" do sign_in user get :show expect(response).to be_success end it "returns a 200 response" do sign_in user get :show expect(response).to have_http_status "200" end end context "as an unauthenticated user" do let(:user) { FactoryBot.create(:user) } let(:other_user) { FactoryBot.create(:user) } let(:project) { FactoryBot.create(:project, owner: other_user) } it "responds successfully" do sign_in user get :show, params: { id: project.id } expect(response).to have_http_status "302" end it "redirect to the sign-in page" do sign_in user get :show, params: { id: project.id } expect(response).to redirect_to root_path end end end
ユーザー入力をテストする
createアクションのテスト
- ログイン済みのユーザーであれば新しいプロジェクトを作成できる
- ゲストであればアクシ ンへのアクセスを拒否される
describe "#create" do context "as an authenticated user" do before do @user = FactoryBot.create(:user) end it "adds a project" do project_params = FactoryBot.attributes_for(:project) sign_in @user expect do post :create, params: { project: project_params } end.to change(@user.projects, :count).by(1) end end # 上記は正常系テストなので、ここに異常系テストを入れるべき。書籍参照 context "as a guest" do it "returns a 302 response" do project_params = FactoryBot.attributes_for(:project) post :create, params: { project: project_params } expect(response).to have_http_status "302" end it "redirects to the sign-in page" do project_params = FactoryBot.attributes_for(:project) post :create, params: { project: project_params } expect(response).to redirect_to "/users/sign_in" end # 以下、書籍にはないが必要では? it "cannot add a project" do expect { post :create, params: { project: project_params } }.not_to change(Project, :count) end end end
updateアクションのテスト
update後、ecpectの記述においてreloadメソッドを呼んでいる点に注意。
こう記述しないと更新後の値が取得できない。
describe"#update"do # ログインしており、オーナーである context "as an authorized user" do let!(:user) { FactoryBot.create(:user) } let!(:project) { FactoryBot.create(:project, owner: user) } # プロジェクトを更新できる it "updates a project" do project_params = FactoryBot.attributes_for(:project, name: "New name") sign_in user patch :update, params: { id: project.id, project: project_params } expect(project.reload.name).to eq "New name" end end # ログインしているが、オーナーではない context "as an unauthorized user" do let!(:user) { FactoryBot.create(:user) } let!(:other_user) { FactoryBot.create(:user) } let!(:project) { FactoryBot.create(:project, owner: other_user, name: "Old name" ) } # プロジェクトを更新できない it "does not update the project" do project_params = FactoryBot.attributes_for(:project, name: "New name") sign_in user patch :update, params: { id: project.id, project: project_params } expect(project.reload.name).to eq "Old name" end # ダッシュボードにリダイレクトする it "redirects to the dashboard" do project_params = FactoryBot.attributes_for(:project, name: "New name") sign_in user patch :update, params: { id: project.id, project: project_params } expect(response).to redirect_to root_path end end # 以下、略 # ログインしていない context "as a guest" do # 302レスポンスを返す it "returns a 302 response" # サインイン画面へリダイレクト it "redirects to the sign-in page" end end
destroyアクションのテスト
createとほぼ同じ。
削除できないは、to_notを使う。
expect { delete :destroy, params: { id: @project.id } }.to_not change(Project, :count)
HTML 以外の出力を扱う
省略