formオブジェクトを用いてリクエストテスト、結合テストを行いリソースの保存を検証したい
class Output
include ActiveModel::Model
attr_accessor :content, :user_id, :book_id, :awareness, :action_plans
def save
awareness = Awareness.new(content: content, book_id: book_id, user_id: user_id)
awareness.save
action_plans.each do |action_plan|
action_plan = ActionPlan.new(time_of_execution: action_plan[:time_of_execution], what_to_do: action_plan[:what_to_do], how_to_do: action_plan[:how_to_do], book_id: book_id, user_id: user_id, awareness_id: awareness.id)
action_plan.save
BookActionPlan.create(book_id: book_id, action_plan_id: action_plan.id)
end
end
end
こんな感じで一度に複数のモデルにデータを挿入するformオブジェクトを生成した。
本を読んだ気づきとアクションプラン(1~3個で変動)を同時に保存することができる。
これを用いてリクエストテストやユニットテストを実装する際にリソースの保存処理の書き方で困った
changeマッチャを使ってモデルのカウントの上昇分を検証する
普通rspecでリソースの保存を検証する際には
expect do
post hoge_path
end.to change(Hoge, :count).by(1)
のようにやる。こうすることでモデルのカウントが増えていることを検証している。
ところが今回のformオブジェクトでは検証すべきモデルが複数あるのでこういうときはどう書くのか?というのがわからなかった。
exampleを複数用意することでも対処は可能だけど
it "投稿に成功するとAwarenessモデルのカウントが1増える" do
expect do
post api_v1_book_outputs_path(book.id), xhr: true, params: {output: output_params}, headers: headers
sleep 2
end.to change(Awareness, :count).by(1)
end
it "投稿に成功するとActionPlanモデルのカウントが3増える" do
expect do
post api_v1_book_outputs_path(book.id), xhr: true, params: {output: output_params}, headers: headers
sleep 2
end.to change(ActionPlan, :count).by(3)
end
こんな感じでexampleを複数はやしてもいいんだけどなんか冗長だなぁと感じていた。
すると以下の記事にヒットした。
andメソッドを使うことで検証内容を増やす
it "投稿に成功するとAwarenessモデルのカウントが1増え、ActionPlanモデルのカウントが3増える" do
expect do
post api_v1_book_outputs_path(book.id), xhr: true, params: {output: output_params}, headers: headers
sleep 2
end.to change(Awareness, :count).by(1).and change(ActionPlan, :count).by(3)
end
こんな感じでandでつなげることでcountの検証を複数同時に行うことができる。
すげぇこんなのあるのか…
私はこれを知った時稲妻が落ちたような気持ちになった。
実際のアプリの環境に照らしても同時に検証できる方がベターなのでこれでコードの可読性もテストとしての安全性も高まった。やったね!