きっかけ
inherited_resources
を導入して、scaffold
で生成したspec
の中、POST create invalid
とPUT update invalid
とupdate
のケースがエラーになったので、調べてみました
環境
- Rails 4.1.4
- Ruby 2.1.2
エラーのケース
it "re-renders the 'new' template" do
# Trigger the behavior that occurs when invalid params are submitted
Journal.any_instance.stub(:save).and_return(false)
post :create, {:journal => { "title" => "invalid title" }}, valid_session
response.should render_template(:new)
end
it "re-renders the 'edit' template" do
journal = Journal.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Journal.any_instance.stub(:save).and_return(false)
put :update, {:id => journal.to_param, :journal => { "title" => "invalid title" }}, valid_session
response.should render_template(:edit)
end
it "updates the requested journal" do
journal = Journal.create! valid_attributes
# Assuming there are no other journals in the database, this
# specifies that the Journal created on the previous line
# receives the :update_attributes message with whatever params are
# submitted in the request.
Journal.any_instance.should_receive(:update).with({ "title" => "MyString" })
put :update, {:id => journal.to_param, :journal => { "title" => "MyString" }}, valid_session
end
内容
Failures:
1) JournalsController PUT update with invalid params re-renders the 'edit' template
Failure/Error: response.should render_template(:edit)
expecting <"edit"> but rendering with <[]>
# ./spec/controllers/journals_controller_spec.rb:122:in `block (4 levels) in <top (required)>'
# -e:1:in `<main>'
2) JournalsController POST create with invalid params re-renders the 'new' template
Failure/Error: response.should render_template(:new)
expecting <"new"> but rendering with <[]>
# ./spec/controllers/journals_controller_spec.rb:78:in `block (4 levels) in <top (required)>'
# -e:1:in `<main>'
3) JournalsController PUT update with valid params updates the requested journal
Failure/Error: Unable to find matching line from backtrace
Exactly one instance should have received the following message(s) but didn't: update
エラーの原因
1,2 の原因
-
inherited_resources
はrespond_with
メソッドを活用している -
respond_with
はResponder
のcall
⇒to_html
⇒navigation_behavior
の順んで呼び出される
https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb#L192
def navigation_behavior(error)
if get?
raise error
elsif has_errors? && default_action
render :action => default_action
else
redirect_to navigation_location
end
end
...
def has_errors?
resource.respond_to?(:errors) && !resource.errors.empty?
end
- エラーがあるかどうかの判定は、
resource.errors.empty?
で判定している - そして、
spec
の中は、Journal.any_instance.stub(:save).and_return(false)
のようにstub
でエラーにさせようとしているが、上記の判定条件に引っかからない為、最後のredirect_to navigation_location
に至ってしまう流れです。
3の原因
-
inherited_resources
はまだupdate_attributes
を使っているので、それに合わせれば良い
対応方法
-
spec
のstub
をやめて、自分のプロジェクトのロジックをみてちゃんとinvalid
のパラメータを渡せばよい - うちの場合、
Journal
のtitle
が必須入力なのんで、title
をnil
に渡すように修正すれば、全spec
パスできました。
it "re-renders the 'new' template" do
# Trigger the behavior that occurs when invalid params are submitted
# Journal.any_instance.stub(:save).and_return(false)
post :create, {:journal => { "title" => nil }}, valid_session
response.should render_template(:new)
end
it "re-renders the 'edit' template" do
journal = Journal.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
# Journal.any_instance.stub(:save).and_return(false)
put :update, {:id => journal.to_param, :journal => { "title" => nil }}, valid_session
response.should render_template(:edit)
end
it "updates the requested journal" do
journal = Journal.create! valid_attributes
# Assuming there are no other journals in the database, this
# specifies that the Journal created on the previous line
# receives the :update_attributes message with whatever params are
# submitted in the request.
Journal.any_instance.should_receive(:update_attributes).with({ "title" => "MyString" })
put :update, {:id => journal.to_param, :journal => { "title" => "MyString" }}, valid_session
end