Rails5からrequest spec推奨になってたのと同時に?、assignsやassert_templateが非推奨になっています。
rails-controller-testingを使う事で、使えてしまうのですが、いずれ使えなくなる可能性があるし、そもそも古い書き方を続けていても未来はないので、使わない形に書き換えてみます。
なぜ非推奨になったかは、こちらのサイトが詳しいので割愛します。
Rails5でコントローラのテストをController specからRequest specに移行する
rails-controller-testing削除
Gemfileの下記を削除
gem 'rails-controller-testing'
$ rspec Failures: 1) Top GET #index 未ログイン behaves like ベースドメイン use index template Failure/Error: expect(response).to render_template('index') NoMethodError: assert_template has been extracted to a gem. To continue using it, add `gem 'rails-controller-testing'` to your Gemfile. Shared Example Group: "ベースドメイン" called from ./spec/requests/top_spec.rb:34 # ./spec/requests/top_spec.rb:22:in `block (4 levels) in'
render_template(=assert_templateかな?)が使えなくなるという事か。
2) Top GET #index 未ログイン behaves like ベースドメイン 新しいスペースが10件取得できる Failure/Error: expect(assigns(:new_spaces)).to match_array new_spaces NoMethodError: assigns has been extracted to a gem. To continue using it, add `gem 'rails-controller-testing'` to your Gemfile. Shared Example Group: "ベースドメイン" called from ./spec/requests/top_spec.rb:34 # ./spec/requests/top_spec.rb:29:in `block (4 levels) in'
assignsが使えないの想定通りだが。
render_template消して良いのか?
悩んだのが、request specとview specの住み分け。
controller specの時は、renderするtemplateが正しい事と、viewに渡す値が正しい事を担保できればOKで、template内はview specで担保と、役割分担が出来ていると理解していた。
そもそもrequest specではrequestをテストする。
結果のHTMLやJSONが返るので、controllerで作られた値がtemplateに埋め込まれて返却される。
controller specでrender_viewsした時と同じ挙動だ。
つまり、request specでtemplate内にcontrollerで渡した値があるかを担保すべきなんだと理解。
じゃあ、view specの役割って何だろう?
templateはデザイナーさんに作って貰ったのを適用する事が多い。
ゆえにHTMLタグを含めたテストは失敗して、直す必要が出て来るので避けている。
この辺は人間なり、Seleniumなりで担保したいので、今までview specに書いたテストをrequest specに移せば良さそう。
書き換え
spec/views/top/index.html.erb_spec.rb はdoとendの中をバッサリ削除。
require 'rails_helper' RSpec.describe 'top/index', type: :view dolet!(:user) { FactoryBot.create(:user) } shared_context 'login' do before { login_user user } end shared_examples_for 'スペースが0件' do it 'もっと見るのパスが含まれない' do render expect(rendered).not_to match("\"#{Regexp.escape(spaces_path)}\"") end it 'スペース作成のパスが含まれる' do render expect(rendered).to match("\"#{Regexp.escape(new_space_path)}\"") end end context '未ログイン' do it_behaves_like 'スペースが0件' end context 'ログイン中' do include_context 'login' it_behaves_like 'スペースが0件' end space_limit = 10 shared_context 'create_spaces' do before do @create_spaces = FactoryBot.create_list(:space, space_limit + 1) @new_spaces = Space.all.order(id: 'DESC').limit(space_limit) end end shared_examples_for "スペースが#{space_limit + 1}件" do it '1番新しいスペース名が含まれる' do render expect(rendered).to match(Regexp.escape(@create_spaces[space_limit].name)) end it "#{space_limit}番目に新しいスペース名が含まれる" do render expect(rendered).to match(Regexp.escape(@create_spaces[1].name)) end it "#{space_limit + 1}番目に新しいスペース名が含まれない" do render expect(rendered).not_to match(Regexp.escape(@create_spaces[0].name)) end it '1番新しいスペースのパスが含まれる' do render expect(rendered).to match("//#{@create_spaces[space_limit].subdomain}.#{Settings['base_domain_link']}") end it "#{space_limit}番目に新しいスペースのパスが含まれる" do render expect(rendered).to match("//#{@create_spaces[1].subdomain}.#{Settings['base_domain_link']}") end it "#{space_limit + 1}番目に新しいスペースのパスが含まれない" do render expect(rendered).not_to match("//#{@create_spaces[0].subdomain}.#{Settings['base_domain_link']}") end it 'もっと見るのパスが含まれる' do render expect(rendered).to match("\"#{Regexp.escape(spaces_path)}\"") end it 'スペース作成のパスが含まれる' do render expect(rendered).to match("\"#{Regexp.escape(new_space_path)}\"") end end context '未ログイン' do include_context 'create_spaces' it_behaves_like "スペースが#{space_limit + 1}件" end context 'ログイン中' do include_context 'login' include_context 'create_spaces' it_behaves_like "スペースが#{space_limit + 1}件" endend
spec/requests/top_spec.rb
rspecで落ちてた下記を削除
it 'use index template' do get root_path, headers: header_hash should render_template('index') end space_limit = 10 it "新しいスペースが#{space_limit}件取得できる" do FactoryBot.create_list(:space, space_limit + 1) new_spaces = Space.all.order(id: 'DESC').limit(space_limit) get root_path, headers: header_hash expect(assigns(:new_spaces)).to match_array new_spaces end
代わりに下記を追加
describe 'GET #index @new_spaces' do shared_context 'スペース作成' do |limit| before { @create_spaces = FactoryBot.create_list(:space, limit) } end shared_examples_for 'ベースドメイン、1番新しいスペース' do it '名前が含まれる' do get root_path, headers: base_headers expect(response.body).to include(@create_spaces[@create_spaces.count - 1].name) end it 'パスが含まれる' do get root_path, headers: base_headers expect(response.body).to include("//#{@create_spaces[@create_spaces.count - 1].subdomain}.#{Settings['base_domain_link']}") end end shared_examples_for "ベースドメイン、#{Settings['new_spaces_limit']}番目に新しいスペース" do it '名前が含まれる' do get root_path, headers: base_headers expect(response.body).to include(@create_spaces[@create_spaces.count - Settings['new_spaces_limit']].name) end it 'パスが含まれる' do get root_path, headers: base_headers expect(response.body).to include("//#{@create_spaces[@create_spaces.count - Settings['new_spaces_limit']].subdomain}.#{Settings['base_domain_link']}") end end shared_examples_for "ベースドメイン、#{Settings['new_spaces_limit'] + 1}番目に新しいスペース" do it '名前が含まれない' do get root_path, headers: base_headers expect(response.body).not_to include(@create_spaces[@create_spaces.count - (Settings['new_spaces_limit'] + 1)].name) end it 'パスが含まれない' do get root_path, headers: base_headers expect(response.body).not_to include("//#{@create_spaces[@create_spaces.count - (Settings['new_spaces_limit'] + 1)].subdomain}.#{Settings['base_domain_link']}") end end shared_examples_for 'ベースドメイン、スペースがない' do it 'ベースドメイン、もっと見るのパスが含まれない' do get root_path, headers: base_headers expect(response.body).not_to include("\"#{spaces_path}\"") end end shared_examples_for 'ベースドメイン、スペースがある' do it 'ベースドメイン、もっと見るのパスが含まれる' do get root_path, headers: base_headers expect(response.body).to include("\"#{spaces_path}\"") end end shared_examples_for 'ベースドメイン' do it 'スペース作成のパスが含まれる' do get root_path, headers: base_headers expect(response.body).to include("\"#{new_space_path}\"") end end shared_examples_for 'スペースが0件' do it_behaves_like 'ベースドメイン、スペースがない' it_behaves_like 'ベースドメイン' end shared_examples_for 'スペースが最大表示数と同じ' do include_context 'スペース作成', Settings['new_spaces_limit'] it_behaves_like 'ベースドメイン、1番新しいスペース' it_behaves_like "ベースドメイン、#{Settings['new_spaces_limit']}番目に新しいスペース" it_behaves_like 'ベースドメイン、スペースがある' it_behaves_like 'ベースドメイン' end shared_examples_for 'スペースが最大表示数より多い' do include_context 'スペース作成', Settings['new_spaces_limit'] + 1 it_behaves_like 'ベースドメイン、1番新しいスペース' it_behaves_like "ベースドメイン、#{Settings['new_spaces_limit']}番目に新しいスペース" it_behaves_like "ベースドメイン、#{Settings['new_spaces_limit'] + 1}番目に新しいスペース" it_behaves_like 'ベースドメイン、スペースがある' it_behaves_like 'ベースドメイン' end context '未ログイン' do it_behaves_like 'スペースが0件' end context 'ログイン中' do include_context 'ログイン処理' it_behaves_like 'スペースが0件' end context '未ログイン' do it_behaves_like 'スペースが最大表示数と同じ' end context 'ログイン中' do include_context 'ログイン処理' it_behaves_like 'スペースが最大表示数と同じ' end context '未ログイン' do it_behaves_like 'スペースが最大表示数より多い' end context 'ログイン中' do include_context 'ログイン処理' it_behaves_like 'スペースが最大表示数より多い' end end
内容は、テスト仕様書を書く時の検証項目を想定して、組み合わせパターンと境界値等で書いています。
実装前にテスト仕様書を作り、テスト駆動にするのが私の理想です。
そして、ユニットテストの正しさを検証する為に手動テストを1回だけ行う。
1度手動でテストした所は、2回目は行わなくても担保できる。
【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/184ff1498e734e24346e2290352a7e27bb298c84