こんなコントローラがありました
class CardsController < ApplicationController
before_action :set_card, only: %i(show edit update destroy)
before_action :set_list, only: %i(new create)
def new
@card = Card.new
end
def create
@card = Card.new(card_params)
if @card.save
redirect_to :root
else
render :new
end
end
(中略)
private
def card_params
params.require(:card).permit(:title, :memo, :list_id)
end
def set_card
@card = Card.find_by(id: params[:id])
end
def set_list
@list = List.find_by(id: params[:list_id])
end
end
こんなルーティングを設定しました
resources :lists, except: %i(index, show) do
resources :cards, except: :index
end
するとこうなりました
list_cards POST /lists/:list_id/cards(.:format) cards#create
new_list_card GET /lists/:list_id/cards/new(.:format) cards#new
edit_list_card GET /lists/:list_id/cards/:id/edit(.:format) cards#edit
list_card GET /lists/:list_id/cards/:id(.:format) cards#show
PATCH /lists/:list_id/cards/:id(.:format) cards#update
PUT /lists/:list_id/cards/:id(.:format) cards#update
DELETE /lists/:list_id/cards/:id(.:format) cards#destroy
Request Specを書きました
RSpec.describe "CardsResponses", type: :request do
describe "cards responses" do
let(:user) { create(:user01) }
let(:list) { create(:list01, user: user) }
let(:card) { create(:card01, list: list) }
let(:valid_params) { attributes_for(:card01, list: list, title: "CARD000") }
(中略)
describe "#create" do
it "responds [302]" do
post list_cards_path(card), params: { card: valid_params }
expect(response).to have_http_status 302
end
end
end
end
結果
1) CardsResponses cards responses as a login user #create responds [302]
Failure/Error: expect(response).to have_http_status 302
expected the response to have status code 302 but it was 200
# ./spec/requests/cards_responses_spec.rb:31:in `block (5 levels) in <main>'
# -e:1:in `<main>'
成功してredirect_to :root
に飛ばされるはずが、
失敗してrender :edit
されてしまっている。
つまりは、createに失敗している。
対処
createのテストコードにbinding.pryをはさみました
describe "#create" do
it "responds [302]" do
post list_cards_path(card), params: { card: valid_params }
binding.pry
expect(response).to have_http_status 302
end
end
(仮定1) list_cards_pathに与える変数が間違っている?
letで設定していたのは以下。
let(:user) { create(:user01) }
let(:list) { create(:list01, user: user) }
let(:card) { create(:card01, list: list) }
POST list_cards_path /lists/:list_id/cards(.:format)
なので、
パラメータとして、:list_id
が求められている。
[3] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> list_cards_path(card)
=> "/lists/1/cards"
あってる?
[4] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> list_cards_path(list, card)
=> "/lists/1/cards.1"
おっとこれはおかしい
[5] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> list_cards_path(list)
=> "/lists/1/cards"
あってる
list has_many cards
の関係なので、お互いのidは関連付けで持ってるから、
どうやらlist、cardどちら指定してもOKみたい。
今回はcardにしておく
(仮定2) 与えてるパラメータが間違っている?
そもそもOKなパラメータって何よ?
private
def card_params
params.require(:card).permit(:title, :memo, :list_id)
end
title, memo, list_id
ね。
に対して、僕が設定したパラメータはこんなんでした。
let(:valid_params) { attributes_for(:card01, list: list, title: "CARD000") }
factory.rb
に登録してあるcard01の関連付けにlistを設定、titleも変えてcard01を呼び出す。
これをpry上で実行すると
[7] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params
=> {:title=>"CARD000",
:memo=>"memo01",
:list=>
#<List:0x00007f8d4f1c32c8
id: 1,
title: "todo01",
user_id: 1,
created_at: Sun, 27 Jan 2019 02:24:20 UTC +00:00,
updated_at: Sun, 27 Jan 2019 02:24:20 UTC +00:00>}
ほーん
require.permitで受け取るパラメータは参照できるのかね?
[8] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:title]
=> "CARD000"
できる
[9] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:memo]
=> "memo01"
できる
[10] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:list_id]
=> nil
!!!!!!!!!!!!!!
できない…
(仮定2のつづき) 関連付けをlist: listで指定したのが間違っていた?
てなわけで、valid_parmas[:list_id]
で参照できるように修正してみる。
(修正前)
let(:valid_params) { attributes_for(:card01, list: list, title: "CARD000") }
(修正後)
let(:valid_params) { attributes_for(:card01, list_id: list.id, title: "CARD000") }
結果
[1] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params
=> {:title=>"CARD000", :memo=>"memo01", :list_id=>1}
お!ちょっと変わった。
:list_id
もしっかり設定されてる。
でも本当に参照できる?
[2] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:title]
=> "CARD000"
できる
[3] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:memo]
=> "memo01"
できる
肝心のlist_idは?
[4] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:list_id]
=> 1
できる!!!!!!!
てなわけで
pryを終了
Finished in 1.26 seconds (files took 0.38429 seconds to load)
50 examples, 0 failures, 2 pending
やっと通りました〜
結論
POST, PUT, PATCH用のパラメータを設定するとき、
コントローラのpermit.requireで指定しているパラメータを
(pry上で)きちんと参照できる形で設定しないと
弾かれてしまうので注意しないといけない
おわり