このページの内容は以下のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。
https://github.com/hkob/hkob_blog
はじめに
Rails Tips の 83 回目です。昨日モデルにユーザを追加したので、request spec を修正し、コントローラが正しく動作するように変更します。
ビューの修正
article と同様にビューを修正していきます。まずは、articles/comments/_form.html.haml
です。commenter
を削り、user_id
を hidden_field
に設定しました。
= form_with model: [@article, @comment] do |form| %table %thead %tr %th 項目 %th 内容 %tbody - %i[body status].each do |attr| %tr %td= form_label_and_error form, @comment, attr %td - case attr - when :status = form.select attr, Comment::VALID_STATUSES - else = form.text_area attr, rows: 10, cols: 80 %tr %td{colspan: 2} = form.hidden_field :article_id = form.hidden_field :user_id = form.submit
また、articles/comments/_comment.html.haml
も commenter
の代わりに user_name
を追加しました。また、編集と削除は自分の所有物の時だけリンクが発生するようにしました。
- return if comment.archived? - if comment == @comment = render "articles/comments/form" - else - %i[user_name body].each do |attr| %p= t_ars Comment, attr %p= comment.send attr - if comment.owned_by? current_user %p= lotfp article_path(@article, comment_id: comment.id), title: "コメントを編集" %p= destroy_lotfp article_comment_path(@article, comment), Comment
comments_spec の修正
comments_spec
の request spec を修正します。所有しないオブジェクトとして、not_mine
を用意しました。また、can_delete
のユーザである can_delete
ユーザでログインしておきます。
RSpec.describe Articles::CommentsController, type: :request do let!(:comment) { comments :can_delete } let!(:object) { comment } let!(:article) { object.article } let!(:attrs) { object.attributes } let(:not_mine) { comments :comment1 } let(:return_path) { article_path(article) } context "login by can_delete" do user_login :can_delete
まず、コメントの追加の修正です。こちらは commenter がなくなったので、不正なパラメータを body に変更しただけです。
describe "POST #create" do before { object.destroy } subject { -> { post article_comments_path(article), params: {comment: attrs} } } context "正しいパラメータに対して" do it_behaves_like "レスポンスコード確認", 302 it_behaves_like "オブジェクトが1増えるか?", Comment it_behaves_like "リダイレクト確認" it_behaves_like "Notice メッセージ確認", "コメントを登録しました。" end context "不正なパラメータに対して" do before { attrs["body"] = "" } it_behaves_like "レスポンスコード確認", 422 it_behaves_like "オブジェクト数が変化しない?", Comment it_behaves_like "Alert メッセージ確認", "コメントの登録に失敗しました。" end end
こちらのテストが通過しないのは、user_id が comment_params に入っていないためです。こちらを修正することで create のテストは通過します。
def comment_params params.require(:comment).permit(:user_id, :body, :article_id, :status) end
次に update のテストを修正します。こちらは所有権の確認があるため、article の時と同様にテストは 3 パターンになります。また、commenter の修正だったテストは status の修正に変更しています。
describe "PATCH #update" do subject { -> { patch article_comment_path(article, one), params: {comment: attrs} } } context "when owned object" do let(:one) { object } context "正しいパラメータに対して" do before { attrs["status"] = "private" } it_behaves_like "レスポンスコード確認", 302 it_behaves_like "オブジェクト属性が変化した?", Comment, :status, "private" it_behaves_like "リダイレクト確認" it_behaves_like "Notice メッセージ確認", "コメントを更新しました。" end context "不正なパラメータに対して" do before { attrs["body"] = "" } it_behaves_like "レスポンスコード確認", 422 it_behaves_like "オブジェクト属性が変化しない?", Comment, :body it_behaves_like "Alert メッセージ確認", "コメントの更新に失敗しました。" end end context "when not owned object" do let(:one) { not_mine } it_behaves_like "レスポンスコード確認", 302 it_behaves_like "rootリダイレクト確認" end end
所有権の確認は article と同様に take_one で行います。これでテストが通過しました。
def take_one @comment = object_from_params_id Comment redirect_to root_path, alert: I18n.t("errors.messages.not_owned") unless @comment.owned_by?(current_user) end
当然ながら destroy のテストも修正しておきます。こちらはすでに通過するはずです。
describe "DELETE #destroy" do subject { -> { delete article_comment_path(article, one) } } context "when owned object" do let(:one) { object } it_behaves_like "レスポンスコード確認", 303 it_behaves_like "オブジェクトが1減るか?", Comment it_behaves_like "リダイレクト確認" it_behaves_like "Notice メッセージ確認", "コメントを削除しました。" end context "when not owned object" do let(:one) { not_mine } it_behaves_like "レスポンスコード確認", 302 it_behaves_like "rootリダイレクト確認" end end
これで全てのテストが通過したと思いましたが、昨日の can_delete article のユーザを変更したせいで、articles_spec のテストが失敗するようになっていました。articles_spec では cannot_delete ユーザでログインする必要がありました。こちらを修正したことで全てのテストが通過しました。
context "when login by cannot_delete" do user_login :cannot_delete
おわりに
これでやりたかったテスト系は大体説明できたのではないかと思います。