【Rspec】ネストされているコントローラーのテストについて。「No route matches」と言わせない書き方
この記事を書いた背景
題名の通りですが、ネストされているコントローラーのテストをRspecで書いていて、ややハマったので、書きました。
ネストの状況はこの通り。
Rails.application.routes.draw do devise_for :users root 'groups#index' resources :users, only: [:edit, :update] resources :groups, except: [:show, :destroy] do resources :messages, only: [:index, :create] end end
今回テストしようとしていたのは、messagesコントローラーです。
ご覧の通り、groupsの下にネストされているので、indexとcreateのroutingが、
GET /groups/:group_id/messages(.:format) messages#index POST /groups/:group_id/messages(.:format) messages#create
このようになっています。
そのため、messageをテストするにも、親となるgroupのidが必要になってくるのですね。
そこの書き方を示していきたいと思います。
開発環境
開発環境は以下の通り。
ruby '2.3.1' 'rails', '~> 5.1.6' 'rspec-rails' '3.7.2' 'factory_girl_rails', "~> 4.4.1" 'devise'
ネストされているコントローラーのテストをRspecで書く(indexアクション)
indexアクションのテストを例に説明していきます。
想定通りのインスタンスが取得できているか、レンダリング先は正しいか、ということをテストします。
まずは全体像を示しておきます。
require 'rails_helper' describe MessagesController do let(:group) { create(:group) } let(:user) { create(:user) } describe '#index' do context 'log in' do before do login_user user get :index, params: { group_id: group.id } end it 'assigns @message' do expect(assigns(:message)).to be_a_new(Message) end it 'assigns @group' do expect(assigns(:group)).to eq group end it 'redners index' do expect(response).to render_template :index end end context 'not log in' do before do get :index, params: { group_id: group.id } end it 'redirects to new_user_session_path' do expect(response).to redirect_to(new_user_session_path) end end end end
つらつらと書いていますが、一番言いたいのはココ!
before do login_user user get :index, params: { group_id: group.id } end
beforeの記述部分の、「get :index, params: { group_id: group.id }」というところです。
なぜこんな書き方なのか?
もしネストしていないモデルのコントローラーだったら、
get :index
これだけで擬似的にアクションを呼び出すことが可能です。
しかし、ネストされているので、親モデルのidがパラメーターとして必要になります。
ゆえに、
params: { group_id: group.id }
で呼び出してあげる必要があるのでした〜。
注意点
ネストしていないモデルで、かつパラメータとしてidが必要なアクションがありますよね。例えば、showアクションとか。
それを呼び出すときは、
#gemのfactory_girl_railsを使用 group = create(:group) get :show, id: group
こんな風に書きます。
筆者は、先ほどのネストされたモデルのアクションを呼び出すときに、同じノリで書いてしまい、プチハマりしました
group = create(:message) get :index, group_id: group
こんな風に。
そうすると、「No route matches」と言われ続けます。
皆さんは筆者と同じ轍を踏まないようにしてくださいね(^^;)