Edited at

開発日記その6〜Rspec controllerテストの修正(主にcreate)


はじめに

Qiita,Rails初心者なので至らないところ多々あります

開発環境はその1の記事を参照ください


開発していく


FactoryBotを上手く動かす


新しく設定を追加する

今までFactoryBot.attributes.forでしか動かなかったものを

rails.helperrequire 'factory_bot'と記述

更にしっかり定義されてるのに動かないFactoryBotには


spec_helper.rb

RSpec.configure do |config|

config.before(:all) do
FactoryBot.reload
end
end

を記述しておく

参考:spec/factories以下に正しいfactoryの定義が存在するのにFactory not registered

springのせいで色々エラー起きてるけど最初から対策するにはどうしたらいいんだろう


FactoryBotのデータの名前を変える

分かりやすくするために名前をただのレシピから変える

factory :name do... endで名前を指定するのだけど,

:nameがモデルの名前と違う時は

factory :different_name, class: :model do...end

の形で記述が必要なので注意


Rspecのbeforeとlet

動作を先に記述しておく時にbeforeやletをよく使う(らしい)のだが,beforeとletはそれぞれ単体で使わないとエラーが出る

beforeはまとめて記述できるが,let!は一文のみ(毎回,一番最初)

letは変数を使う前にその変数に代入したりしてくれる(直前)

letだと直前に動く性質上上手く動かないこともあるらしいので注意する(そういう時はlet!にすると通る)

参考:RSpecのletを使うのはどんなときか?(翻訳)


controller#indexの修正


Recipe.allで想定したデータが入っているかのテスト

# 今までは一個ずつデータを作ってた

@recipe = post(:create, ...)
@other_recipe = post(:create, ...)

...

it '@recipeにすべてのレシピを割り当てる' do
expect(assigns(:recipes)).to eq match_array([@recipe,
@other_recipe])
end

# 下記の@recipe同様テスト環境なので色々データが入ってる
# controllerの方の変数に入るものとは別のデータも多いのでエラーが出てた

# 解決方法(後で唯一性に引っかかるかもしれないので暫定)
it '@recipeにすべてのレシピを割り当てる' do
recipes = create_list(:recipe_with_name_and_valid_url, 3)
expect(assigns(:recipes)).to eq recipes
end

# => Green

assigns(key)match_arrayを使えば唯一性の問題もクリアできるかと思ったけど

     Failure/Error: expect(assigns(:recipes)).to match_array([assigns(:recipe), assigns(:other_recipe)])

expected collection contained: [#<Recipe id: 2, name: "Yahoo", url: "yahoo.com", created_at: "2018-10-09 16:14:14", updated_at: "2018-10-09 16:14:14">, nil]
actual collection contained: [#<Recipe id: 1, name: "Google", url: "https://www.google.com/", created_at: "2018-10-09 16:14:14", u...e: "Yahoo", url: "yahoo.com", created_at: "2018-10-09 16:14:14", updated_at: "2018-10-09 16:14:14">]
the missing elements were: [nil]
the extra elements were: [#<Recipe id: 1, name: "Google", url: "https://www.google.com/", created_at: "2018-10-09 16:14:14", updated_at: "2018-10-09 16:14:14">]

上書きされてるような挙動だったので一旦保留にすることにした

assignsの使い方も怪しいので.

参考:RSpecにおけるFactoryGirlまとめ


controller#createの修正

ちゃんとコントローラー書いても案の定テストはエラーになるのでテストを治す


post createするとテストが大量に作られる

大量に作られると言うか前のデータが残ってしまう様子.

対策としては


rails_helper.rb

RSpec.configure do |config|

config.use_transactional_fixtures = true
end

を記述してrails db:resetすると解決する


save確認のテスト

@recipe = post(:create, params: {recipe: {@valid_recipe}})

...

it 'saveできる' do
expect(@recipe).to change(Recipe, :count).by(1)
end

# => expected `Recipe.count` to have changed by 1, but was not given a block

# 解決方法
it 'DBに新しいレシピが登録される' do
expect do
post(:create, params: {recipe: @recipe_with_name_and_valid_url})
end.to change(Recipe, :count).by(1)
end

# =>Green


redirectに使うidを取得したい

it 'showにredirect' do

expect(response).to redirect_to recipe_path(@recipe.id)
end

# => @recipeの中にはidがないのでエラーがでる
# (pryで見てみるとテスト環境なのでネストの奥深くに置いてあったけど取り出せなかった)

# 解決方法
it 'showにredirect' do
expect(response).to redirect_to recipe_path(assigns(:recipe).id)
end

# => Green

これでcreateのテストはまだ実装してない部分以外は無事に通り,未実装部分のテストも想定したエラーを出してるので一旦おしまい


おわり


進捗

・controller実装

・index, show, createの修正完了


次の予定

・controller#destroyの修正

・modelの実装


さいごに

テスト,一週間くらいかけて書いたので直すのも一苦労でした

調べても出ないのが辛い(調べ方が悪い)

もうテスト駆動開発ではなくなってると思うのですけど完成まで頑張ってこうと思います

その7