request spec の雛形準備 - 不定期刊 Rails App を作る(20)

RailsApp Rails RSpec 2018年 11月 30日

request spec のテンプレート作成

kurasus_controller.rb に対して,一通りの request spec が作成できたので,これをベースに template を作成する.

  1. kurasus_spec.rb を lib/templates/rspec/integration/request_spec.rb にコピーする.
  2. cp spec/requests/kurasus_spec.rb lib/templates/rspec/integration/request_spec.rb
  3. クラス名などを erb を使って書き換える.最終的に生成した request_spec.rb を掲載しておく.とりあえず現状のもので今後修正する可能性はある.
  4. require 'rails_helper'
    
    RSpec.describe :<%= class_name.pluralize %>, type: :request do
      include Devise::Test::IntegrationHelpers
    
      let(:return_path) { <%= plural_table_name %>_path(edit_mode: true) }
      context 'login by hkob' do
        let!(:object) { <%= singular_table_name %>_factory :factory_one }
        let!(:others) { <%= singular_table_name %>_factory :factory_other }
        let(:not_mine) { <%= singular_table_name %>_factory :factory_not_mine }
        teacher_login :hkob
    
        describe 'GET #index' do
          context 'normal mode' do
            subject { -> { get <%= plural_table_name %>_path } }
            it_behaves_like :response_status_check, 200
            it_behaves_like :response_body_includes, %w[XXX一覧: 表示される文字列]
            it_behaves_like :response_body_not_includes, '表示されない文字列'
          end
    
          context 'edit mode' do
            subject { -> { get <%= plural_table_name %>_path(edit_mode: true) } }
            it_behaves_like :response_status_check, 200
            it_behaves_like :response_body_includes, %w[XXX一覧: 表示される文字列]
          end
        end
    
        describe 'GET #new' do
          subject { -> { get new_<%= singular_table_name %>_path } }
          it_behaves_like :response_status_check, 200
        end
    
        describe 'POST #create' do
          before { @attrs = object.attributes; object.destroy }
          subject { -> { post <%= plural_table_name %>_path, params: {<%= singular_table_name %>: @attrs}} }
    
          context '正しいパラメータに対して' do
            it_behaves_like :response_status_check, 302
            it_behaves_like :increment_object_by_create, <%= class_name %>
            it_behaves_like :redirect_to
            it_behaves_like :flash_notice_message, 'XXXを登録しました.'
          end
    
          context '不正なパラメータに対して' do
            before { @attrs['name'] = nil }
            it_behaves_like :response_status_check, 200
            it_behaves_like :not_increment_object_by_create, <%= class_name %>
            it_behaves_like :flash_alert_message, 'XXXが登録できません.'
          end
        end
    
        describe 'GET #edit' do
          context 'owned object' do
            subject { -> { get edit_<%= singular_table_name %>_path(object) } }
            it_behaves_like :response_status_check, 200
            it_behaves_like :response_body_includes, '2300'
          end
    
          context 'not owned object' do
            subject { -> { get edit_<%= singular_table_name %>_path(not_mine) } }
            it_behaves_like :response_status_check, 302
            it_behaves_like :redirect_to
            it_behaves_like :flash_alert_message, 'XXXを編集する権限がありません.'
          end
        end
    
        describe 'PATCH #update' do
          before { @attrs = object.attributes }
    
          context 'owned object' do
            subject { -> { patch <%= singular_table_name %>_path(object), params: {<%= singular_table_name %>: @attrs} } }
    
            context '正しいパラメータに対して' do
              before { @attrs['name'] = 'new name' }
              it_behaves_like :response_status_check, 302
              it_behaves_like :change_object_value_by_update, <%= class_name %>, :name, 'new name'
              it_behaves_like :redirect_to
              it_behaves_like :flash_notice_message, 'XXXを更新しました.'
            end
    
            context '不正なパラメータに対して' do
              before { @attrs['name'] = nil }
              it_behaves_like :response_status_check, 200
              it_behaves_like :not_change_object_value_by_update, <%= class_name %>, :name
              it_behaves_like :flash_alert_message, 'XXXが更新できません.'
            end
          end
    
          context 'not owned object' do
            subject { -> { put <%= singular_table_name %>_path(not_mine), params: {<%= singular_table_name %>: @attrs} } }
            it_behaves_like :response_status_check, 302
            it_behaves_like :redirect_to
            it_behaves_like :flash_alert_message, 'XXXが更新できません.'
          end
        end
    
        describe 'DELETE #destroy' do
          context 'owned object' do
            subject { -> { delete <%= singular_table_name %>_path(object), params: {<%= singular_table_name %>: @attrs} } }
            it_behaves_like :response_status_check, 302
            it_behaves_like :decrement_object_by_destroy, <%= class_name %>
            it_behaves_like :redirect_to
            it_behaves_like :flash_notice_message, 'XXXを削除しました.'
          end
    
          context 'now owned object' do
            subject { -> { delete <%= singular_table_name %>_path(not_mine), params: {<%= singular_table_name %>: @attrs} } }
            it_behaves_like :response_status_check, 302
            it_behaves_like :redirect_to
            it_behaves_like :flash_alert_message, 'XXXを削除する権限がありません.'
          end
        end
    
        describe 'GET #show' do
          context 'owned object' do
            subject { -> { get <%= singular_table_name %>_path(object) } }
            it_behaves_like :response_status_check, 200
            it_behaves_like :response_body_includes, %w[XXX表示: YYY]
          end
    
          context 'now owned object' do
            subject { -> { get <%= singular_table_name %>_path(not_mine) } }
            it_behaves_like :response_status_check, 302
            it_behaves_like :redirect_to
            it_behaves_like :flash_alert_message, 'XXXを表示する権限がありません.'
          end
        end
      end
    
      context 'not login' do
        describe 'GET #index' do
          subject { -> { get <%= plural_table_name %>_path } }
          it_behaves_like :response_status_check, 302
          it_behaves_like :response_body_not_includes, %w[表示されない文字列]
        end
      end
    end

request spec のまとめ

request spec は以前の controller spec の代わりになるものである. controller spec と違って内部のオブジェクトまではテストせず,あくまでリクエストの成功などを確認するだけに限定された. このため,controller spec よりもテストがやりやすくなったと思う. 以下に今回の kurasus_spec.rb のテスト結果をまとめて示しておく.

Kurasus
  login by hkob
    GET #index
      normal mode
        behaves like response_status_check
          response should be 200
        behaves like response_body_includes
          response body includes ["クラス一覧:", "小林弘幸", "2300"]
        behaves like response_body_not_includes
          response body does not include 5300
      edit mode
        behaves like response_status_check
          response should be 200
        behaves like response_body_includes
          response body includes ["クラス一覧:", "小林弘幸", "2300", "5300"]
    GET #new
      behaves like response_status_check
        response should be 200
    POST #create
      正しいパラメータに対して
        behaves like response_status_check
          response should be 302
        behaves like increment_object_by_create
          should change `Kurasu.count` by 1
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_notice_message                                                                                                                                                 should eq "クラスを登録しました."
      不正なパラメータに対して
        behaves like response_status_check
          response should be 200
        behaves like not_increment_object_by_create
          should not change `Kurasu.count`
        behaves like flash_alert_message
          should eq "クラスが登録できません."
    GET #edit
      owned object
        behaves like response_status_check
          response should be 200
        behaves like response_body_includes
          response body includes 2300
      not owned object
        behaves like response_status_check
          response should be 302
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_alert_message
          should eq "クラスを編集する権限がありません."
    PATCH #update
      owned object
        正しいパラメータに対して
          behaves like response_status_check
            response should be 302
          behaves like change_object_value_by_update
            should change `klass.find(object.id).send(key)` from "2300" to "new name"
          behaves like redirect_to
            should redirect to "/kurasus?edit_mode=true"
          behaves like flash_notice_message
            should eq "クラスを更新しました."
        不正なパラメータに対して
          behaves like response_status_check
            response should be 200
          behaves like not_change_object_value_by_update
            should not change `klass.find(object.id).send(key)`
          behaves like flash_alert_message
            should eq "クラスが更新できません."
      not owned object
        behaves like response_status_check
          response should be 302
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_alert_message
          should eq "クラスが更新できません."
    DELETE #destroy
      owned object
        behaves like response_status_check
          response should be 302
        behaves like decrement_object_by_destroy
          should change `Kurasu.count` by -1
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_notice_message
          should eq "クラスを削除しました."
      now owned object
        behaves like response_status_check
          response should be 302
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_alert_message
          should eq "クラスを削除する権限がありません."
    GET #show
      owned object
        behaves like response_status_check
          response should be 200
        behaves like response_body_includes
          response body includes ["クラス表示:", "2300", "(小林弘幸)"]
      now owned object
        behaves like response_status_check
          response should be 302
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_alert_message
          should eq "クラスを表示する権限がありません."
  not login
    GET #index
      behaves like response_status_check
        response should be 302
      behaves like response_body_not_includes
        response body does not include ["クラス一覧:", "小林弘幸", "2300", "5300"]

長くなったので今日はここまで