[RSpec] ネストしたルーティングのPOST, PUT(PATCH)のテストが大変だったというお話

#こんなコントローラがありました
```ruby:
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
```

# こんなルーティングを設定しました

```ruby:
  resources :lists,       except: %i(index, show) do
    resources :cards,     except: :index
  end
```

# するとこうなりました
```ruby:
    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を書きました

```ruby:
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
```

#結果
```ruby:
  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をはさみました

```ruby:
    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
```

# （仮定１） list_cards_pathに与える変数が間違っている？

letで設定していたのは以下。

```ruby:
    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`が求められている。

```ruby:
[3] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> list_cards_path(card)
=> "/lists/1/cards"
```

あってる？

```ruby:
[4] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> list_cards_path(list, card)
=> "/lists/1/cards.1"
```

おっとこれはおかしい

```ruby:
[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なパラメータって何よ？

```ruby:cards_controller
  private

  def card_params
    params.require(:card).permit(:title, :memo, :list_id)
  end
```
`title, memo, list_id`ね。


に対して、僕が設定したパラメータはこんなんでした。

```ruby:
    let(:valid_params)    { attributes_for(:card01, list: list, title: "CARD000") }
```

`factory.rb`に登録してあるcard01の関連付けにlistを設定、titleも変えてcard01を呼び出す。

これをpry上で実行すると

```ruby:
[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で受け取るパラメータは参照できるのかね？

```ruby:
[8] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:title]
=> "CARD000"
```

できる

```ruby:
[9] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:memo]
=> "memo01"
```

できる

```ruby:
[10] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:list_id]
=> nil
```

<b>！！！！！！！！！！！！！！</b>
<br />

できない…

# （仮定2のつづき） 関連付けをlist: listで指定したのが間違っていた？

てなわけで、`valid_parmas[:list_id]`で参照できるように修正してみる。

（修正前）

```ruby:
    let(:valid_params)    { attributes_for(:card01, list: list, title: "CARD000") }
```

（修正後）

```ruby:
    let(:valid_params)    { attributes_for(:card01, list_id: list.id, title: "CARD000") }
```


#結果
```ruby:
[1] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params
=> {:title=>"CARD000", :memo=>"memo01", :list_id=>1}
```

お！ちょっと変わった。
`:list_id`もしっかり設定されてる。

でも本当に参照できる？

```ruby:
[2] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:title]
=> "CARD000"
```

できる

```ruby:
[3] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:memo]
=> "memo01"
```

できる

<br />
肝心のlist_idは？
<br />
<br />

```ruby:
[4] pry(#<RSpec::ExampleGroups::CardsResponses::CardsResponses::AsALoginUser::Create>)
> valid_params[:list_id]
=> 1
```
<br />
<b>できる！！！！！！！</b>
<br />

# てなわけで
pryを終了

```ruby:
Finished in 1.26 seconds (files took 0.38429 seconds to load)
50 examples, 0 failures, 2 pending
```

やっと通りました〜

#結論
POST, PUT, PATCH用のパラメータを設定するとき、
コントローラのpermit.requireで指定しているパラメータを
（pry上で）きちんと参照できる形で設定しないと
弾かれてしまうので注意しないといけない

<br />
おわり
<br />