5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Request Specでテストを書くときはStrong Parametersを通る前のパラメーターも意識しましょう

Last updated at Posted at 2018-05-24

Ruby on Railsを使い始めて2ヶ月の初心者がControllerのテストシナリオを書いたときにハマった悲しい事象の共有。

前提条件

  • RailsはJSONを吐くWebAPI
  • rails (5.1.6)
  • rspec-core (3.7.1)
  • factory_bot_rails (4.8.3)

テスト対象のController

よくあるControllerです。サンプルで :create:update の2つだけ。本来は :index :show :destroy もあります。

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      render json: @user
    else
      render json: {}
    end
  end

  def update
    @user = User.find_by(params[:id])
    if @user
      if @user.update(user_params)
        render json: @user
      else
        render json: {}
      end
    else
      render json: {}
    end
  end

  private
  
  def user_params
    params.require(:users).permit(:name, :age)
  end
end

作ってあるroutes.rb

シンプルに。

Rails.application.routes.draw do

  resources :users

end

rails routes した結果も一応

Prefix Verb   URI Pattern          Controller#Action
# 略
       POST   /users(.:format)     users#create
       PATCH  /users/:id(.:format) users#update
# 略

書いたテストコード

require 'rails_helper'

describe UsersController, type: :controller do

  # rails_helper内で
  # config.include FactoryBot::Syntax::Methods
  # を設定している前提
  let(:user) { create(:user) }
  
  describe 'Post #create' do
    let(:param) do
      {
        users: {
          name: 'Alice',
          age: 21
        }
      }
    end
    
    context 'when send correct parameters' do
      let(:http_response) do
        post :create, params: param
      end

      it 'responds status "200 OK"' do
        expect(http_response.status).to eq 200  # test OK
      end
    end
  end
  
  describe 'Patch #update' do
    let(:param) do
      {
        users: {
          id: users.id
          name: 'Bob',
          age: 30
        }
      }
    end

    context 'when send correct parameters' do
      it 'responds status "200 OK"' do
        expect(http_response.status).to eq 200  # test NG
      end
    end
  end
end

んでテストNG時に表示されるエラーメッセージがこれ

ActionController::UrlGenerationError:
       No route matches {:action=>"update", :controller=>"users", :users=>{:id=>2, :name=>"Bob", :age=>30}}

ルーティングがうまくできていない模様。
updateに必要なパラメーターはIDだけだし、ちゃんと渡してるのに…

解決策

上の状態だとルーティングが必要としているパラメーターが渡せていません。
URI Patternの :id とは、Strong Parametersを通る前のパラメーターです。

  # 略
  describe 'Patch #update' do
    let(:param) do
      {
        id: users.id,  # これを追加
        users: {
        #  id: users.id  # これは削除
          name: 'Bob',
          age: 30
        }
      }
    end
  # 略

わかってしまえば簡単なのに、これで半日融かしました。
正確にやるなら、nameやageもidと同じくusersの外にも指定してあげるといいと思います。

ご自愛ください。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?