🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Q&A

解決済

2回答

1417閲覧

RSpecでコメント投稿機能をテスト

退会済みユーザー

退会済みユーザー

総合スコア0

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

0グッド

1クリップ

投稿2021/02/14 15:36

編集2021/02/14 15:48

##実現したいこと
コメント機能のテストを実現したいです。
2日間、調べて修正しましたが実現できませんでした。
お手数をおかけ致しますがご理解がある方はヒントでもあれば、
お教えをお願い致します。

前提

作成された掲示板にコメントを投稿致します。
しかし、
コメントデータの設定ができず、下記のエラーメッセージが発生致しました。

発生している問題・エラーメッセージ

Failure/Error: params.require(:comment).permit(:board_id, :name, :comment) ActionController::ParameterMissing: param is missing or the value is empty: comment # ./app/controllers/comments_controller.rb:23:in `comment_params' # ./app/controllers/comments_controller.rb:3:in `create' # /usr/local/bundle/gems/rails-controller-testing-1.0.5/lib/rails/controller/testing/template_assertions.rb:62:in `process' # /usr/local/bundle/gems/rails-controller-testing-1.0.5/lib/rails/controller/testing/integration.rb:16:in `block (2 levels) in <module:Integration>' # ./spec/controllers/comments_controller_spec.rb:7:in `block (4 levels) in <top (required)>'

該当のソースコード

#####コメント投稿テストコード[comments_controller_spec.rb]

Rails

1require 'rails_helper' 2 3RSpec.describe CommentsController, type: :controller do 4 describe 'comment#create' do 5 context 'コメントを投稿が成功した場合' do 6 before do 7 post(:create, params: { 8 board: { 9 name: 'tanaka', 10 title: 'Ruby on Rails 5', 11 body: 'Hello Rails', 12 } 13 }) 14 15 @comment = FactoryBot.build(:comment) 16 post comments_path, params: { 17 board_id: @comment.board_id, 18 name: @comment.name, 19 comment: @comment.comment, 20 } 21 end 22 23 it '掲示板のコメント欄にリダイクトされること' do 24 expect(response).to redirect_to @comment.board 25 end 26 end 27 end 28end

#####コメント投稿機能[comments_controller.rb]

class CommentsController < ApplicationController def create comment = Comment.new(comment_params) if comment.save flash[:notice] = 'コメントを投稿しました。' redirect_to comment.board else flash[:comment] = comment flash[:error_messages] = comment.errors.full_messages redirect_back fallback_location: comment.board end end def destroy comment = Comment.find(params[:id]) comment.delete redirect_to comment.board, flash: { notice: 'コメントが削除されました。' } end private def comment_params params.require(:comment).permit(:board_id, :name, :comment) end end

#####掲示板関連機能[boards_controller.rb]

class BoardsController < ApplicationController # 各アクション実行前に動作、onlyの配列の順序に注意 before_action :set_target_board, only: %i[show edit update destroy] def index @boards = params[:tag_id].present? ? Tag.find(params[:tag_id]).boards : Board.all @boards = @boards.page(params[:page]) end def new @board = Board.new end def create board = Board.new(board_params) if board.save flash[:notice] = '「#{board.title}」の掲示板を作成しました。' redirect_to board else redirect_to new_board_path, flash: { board: board, error_messages: board.errors.full_messages } end end def show @comment = Comment.new(board_id: @board.id) end def edit @board.attributes = flash[:board] if flash[:board] end def update if @board.update(board_params) redirect_to @board # オブジェクトで記述すると、id指定で表示されるため。 else redirect_to :back, flash: { board: @board, error_messages: @board.errors.full_messages } end end def destroy @board.destroy redirect_to boards_path, flash: { notice: '「#{@board.title}」の掲示板が削除されました。' } end private def board_params # strongparameterで取得 params.require(:board).permit(:name, :title, :body, tag_ids: []) end # 1件分のレコードを取得 def set_target_board @board = Board.find(params[:id]) end end

#####rails routesの結果

mypage GET /mypage(.:format) users#me login POST /login(.:format) sessions#create logout DELETE /logout(.:format) sessions#destroy root GET / home#index users POST /users(.:format) users#create new_user GET /users/new(.:format) users#new boards GET /boards(.:format) boards#index POST /boards(.:format) boards#create new_board GET /boards/new(.:format) boards#new edit_board GET /boards/:id/edit(.:format) boards#edit board GET /boards/:id(.:format) boards#show PATCH /boards/:id(.:format) boards#update PUT /boards/:id(.:format) boards#update DELETE /boards/:id(.:format) boards#destroy comments POST /comments(.:format) comments#create comment DELETE /comments/:id(.:format) comments#destroy

試したこと

エラーメッセージの内容の通りに、テストコードのパラメータの内容が不足していると思い、
直接内容を記述。下記コード内容から修正作業を開始。

post(:create, params: {  comment: { board_id: 1,   name: 'tanaka',   comment: 'こんにちは, } })

続いて、ルートで実施。

post comments_path, params: {  board_id: 1,  name: 'tanaka',  comment: 'こんにちは, }

しかし、上記のテストコードの様に
FactoryBotでテストデータを作成後、実施するもエラーメッセージは同じ内容で失敗。

参考にした記事の一例
https://teratail.com/questions/271147

補足情報(FW/ツールのバージョンなど)

[作業環境]
macOS BigSur 11.2
Docker 20.10
Ruby 2.4.5
Rails 5.2.2
RSpec 3.9.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

suama

2021/02/15 07:14

テストだけうまく行かない状態ですね? 通常の画面からの投稿はうまく登録できている状態であれば、specでのパラメータを直せば良さそうですね。
退会済みユーザー

退会済みユーザー

2021/02/15 09:58 編集

suama 様 いつもお世話になっております。 今回も宜しくお願い致します。 はい、おしゃる通りでございます。 作成したテストのみ通らないです。 関連すると予想したファイルを元に上記の試したことを行いました。 ですが、どのようにパラメータを修正すれば、 良いかで止まってしまいました。 comment.rb[commentのモデルファイル] # == Schema Information # # Table name: comments # # id :integer not null, primary key # comment :text(65535) not null # name :string(255) not null # created_at :datetime not null # updated_at :datetime not null # board_id :integer # # Indexes # # index_comments_on_board_id (board_id) # # Foreign Keys # # fk_rails_... (board_id => boards.id) # class Comment < ApplicationRecord  belongs_to :board  validates :name, presence: true, length: { maximum: 10 }  validates :comment, presence: true, length: { maximum: 1000 } end
suama

2021/02/15 13:59

ありがとうございます! 手順としては、おそらく、before do ... end のところで、先にBoadのデータを登録し、その後にBoadの値を使ってCommentのコントローラにPOSTしてCommentを作りたいということですよね? すでに回答が1つありますが、わたしも添えてみますね。
退会済みユーザー

退会済みユーザー

2021/02/15 14:20

suama 様 お忙しい中、ご対応頂きありがとうございます! おしゃる通りです。 Boardのデータ登録後、 そのBoard値を使用してCommentを作成するのが実現したいことです。
suama

2021/02/15 14:33

基本的に、Rspecのテストというのは、***_spec.rb 1つの実行に対して、実行し終わったらデータをクリアにします。 なので、別のspecで作ったデータを、別のspecに利用するというのは、あまり行いません。自動テストの時にゴミデータになってしまうからです。 Boad.rb のモデルのソースを貼っていただけたら、ひとまず回答は追記できますが、それも現場でメンターしているわけではないので、参考程度にしてくださいませ。
退会済みユーザー

退会済みユーザー

2021/02/15 14:39

suama 様 ご返信ありがとうございます。 そのような方法流れが基本なのですね。 大変勉強になりありがとうございます。 また、 ご指示の通り、モデルのソースを記述致しました。 ご確認をお願い致します。 今回もRSpecに関する事を、 ご指摘とご教授してくださり大変助かります!
guest

回答2

0

ベストアンサー

こんばんは。質問へのお答え、ありがとうございます。
パラメータの件ではなくて、Specで書いている流れを見て、ちょっとエスパーして回答してみます。

このspecで想定している処理の流れは?

  • まず、BoardsController#create を呼び出してBoardのデータを1つ作りたい
  • つぎに、そのBoardの値を使って、FactoryBot.build(:comment) した comment用のデータにboardのIDを紐づけたい
  • そして、パラメータを作って、CommentsController#create に送信して、commentデータを正しくデータベースに登録したい

こんな処理でしょうか?
特に、「まず、BoardsController#create を呼び出してBoardのデータを1つ作りたい」という点、あっていますでしょうか?

想定している処理に対して実際出ているエラーは?

ですがエラーで ./spec/controllers/comments_controller_spec.rb:7 と出ていますよね。

specの7行目の処理は、確かに post(:create, params: {board:.... として、BoardsController#createにデータを送信したいように見えます。

でも、このデータは、実際は CommentsController#create に送られてしまっています。
そのため、 ./app/controllers/comments_controller.rb:23:in `comment_params' というメッセージ通り、CommentsController#create 側で「パラメータが合っていないよ!」と怒られているわけです。

また、このspecは RSpec.describe CommentsController, type: :controller do と宣言されています。
なので、 post(...) や get と記載した時には、宛先は必ず CommentsControllerになります。

パラメータだけ直せばうまくいく?

ここに関しては、どういう処理をしたいかによります。

もし、CommentのデータにはBoardデータは必須であるとか、存在するBoardに関連付けしないといけないとかの制限があれば、何にしてもBoardのデータを1つ用意しないといけませんね。

この時、

  • A. BoardController#create経由で作る
  • B. あるいはFactoryBotや直接Board.create(...)で作る

かで方法は変わります。
もしBoardとCommentのモデルのコードが分かれば、Bの方法でテストの中でBoardのデータを用意することもできます。

B案でやってみる

モデルの情報をいただきましたので、手元で動かせてるわけではないですが、参考までに書いてみます。
エラーが出るかもしれませんが、あとは1つ1つ確認しながら進めてみてください。

ruby

1require "rails_helper" 2 3# ここで「CommentsControllerについてのテストだよ!」と宣言してます 4RSpec.describe CommentsController, type: :controller do 5 describe "comment#create" do 6 context "コメントを投稿が成功した場合" do 7 before do 8 # まずBoardのデータを作る 9 10 # この方法はダメ! 11 # post(:create, params: { 12 # board: { 13 # name: 'tanaka', 14 # title: 'Ruby on Rails 5', 15 # body: 'Hello Rails', 16 # } 17 # }) 18 19 # CommentsControllerのspecなので、BoardsController#createを呼び出せません 20 # なので、ここで BoardsController#create中でやっているのと同じことを書いて、直に掲示板の 21 # データを作成します 22 board_params = { 23 name: "tanaka", 24 title: "Ruby on Rails 5", 25 body: "Hello Rails", 26 } 27 # new -> saveを同時に処理しています 28 @board = Board.create!(board_params) 29 30 # ここからがCommentsController#create への本来のPOST 31 # postのあとにパラメータを添えるだけでよいです 32 # パラメータは入れ子になるので注意 33 @comment = FactoryBot.build(:comment) 34 post :create, params: { 35 comment: { 36 board_id: @board.id, 37 name: @comment.name, 38 comment: @comment.comment, 39 }, 40 } 41 end 42 43 it "掲示板のコメント欄にリダイクトされること" do 44 # @comment.board は、実際はコメントに紐付けた@boardと同じになるはず 45 # redirect_to のあとは実際のパスの形式でないと多分ダメ 46 # これは多分エラー 47 # expect(response).to redirect_to @comment.board 48 expect(response).to redirect_to "/boards/#{@board.id}" 49 end 50 end 51 end 52end

投稿2021/02/15 14:22

編集2021/02/16 00:52
suama

総合スコア1997

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/02/15 14:35 編集

suama 様 ありがとうございます。 suama 様の想定している内容通りでございます。 また、エラーの原因が理解ができて大変勉強になりました。 呼ばれているコントローラーが、 そもそも違うのに気づいておりませんでした。 お手数をおかけしますが、 Bの方法をお教え願えますでしょうか? 下記がモデルのコードになります。 Boardのモデル[board.rb] # == Schema Information # # Table name: boards # # id :integer not null, primary key # body :text(65535) # name :string(255) # title :string(255) # created_at :datetime not null # updated_at :datetime not null # class Board < ApplicationRecord  has_many :comments, dependent: :delete_all  has_many :board_tag_relations, dependent: :delete_all  has_many :tags, through: :board_tag_relations  validates :name, presence: true, length: { maximum: 10 }  validates :title, presence: true, length: { maximum: 30 }  validates :body, presence: true, length: { maximum: 1000 } end Commentのモデル[comment.rb] # == Schema Information # # Table name: comments # # id :integer not null, primary key # comment :text(65535) not null # name :string(255) not null # created_at :datetime not null # updated_at :datetime not null # board_id :integer # # Indexes # # index_comments_on_board_id (board_id) # # Foreign Keys # # fk_rails_... (board_id => boards.id) # class Comment < ApplicationRecord  belongs_to :board  validates :name, presence: true, length: { maximum: 10 }  validates :comment, presence: true, length: { maximum: 1000 } end
退会済みユーザー

退会済みユーザー

2021/02/16 00:52

suama 様 無事、テストが成功しました! ありがとうございます! コメントも記述して頂き、勉強になります。 また、機会がありましたらその際は宜しくお願い致します。
guest

0

期待しているのはparams.require(:comment).permit(:board_id, :name, :comment)
なのにspecでは1params: { board: { name: 'tanaka', title: 'Ruby on Rails 5', body: 'Hello Rails', }
全く違うじゃないですか。params: { comment: { board_id: ***, name: 'Ruby on Rails 5', comment: 'Hello Rails', }
としてみて。 *** は この実行前に Boadを作成して、そのidね。

投稿2021/02/15 11:29

winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問