Posted at

Railsのコントローラーのspecを書くとき、subject(:response)と書いてみる

More than 3 years have passed since last update.


subject(:response)

僕はRailsでController書いている時に一つのactionについて、こんな風にspecを書く事が多いです。

subject(:response) do # ←ココ

get :index
end

it { expect(response.status).to eq 200 }

これは普通に考えると、rspec-railsで自動的にresponseを作ってるし、既にあるものを上書きしているようで気持ち悪いところもあるんですが、get(post, putなども同様)の返り値も同じなのでまあ従来と同じようにresponseを使ってテストできます。


なぜわざわざsubject(:response)と書くのか

まずsubjectを使うと「ここではこれをテストするんだな」と分かりやすくて個人的に好きというのがあります。

その上でsubject(:response)と書かずにsubjectとした場合はspecはこんな感じになるかと思います

# subject(:response)ってやらない場合

subject do
get :index
end

# これだとsubjectがresponseだって分かりにくい
it { expect(subject.status).to eq 200 }

# これだと既に書いてあるgetと同じもの書くのが面倒(渡す値多いとなおさら)
it do
get :index
expect(response.status).to eq 200
end

# これだとわざわざ一回subjectと書くのが面倒
it do
subject
expect(response.status).to eq 200
end

それに対して、subject(:response)とするとitの中でresponseが呼ばれた時にsubjectの中身が走って、必要な時にgetをやってくれるのでなんだか良いと思ってます。

subject(:response) do

get :index
end

it { expect(response.status).to eq 200 } # responseが呼ばれた時にsubjectのブロックが実行される

というわけでsubject(:response)ってやっておくとコントローラーのspecを書いてる時の細かい悩みが解決する…気がしてます。

なんかrspec-railsなどの挙動で勘違いしているだろうか。


その他

ここではRailsコントローラーの中でもactionのunitテストを書く場合を考えてます。

Sinatra、Padrinoなどでも同様に出来るとは思いますが実際に試してないです。