今回私、こちらのエラーで死ぬほどひっかかりました。
※途中長々と書いてますが見当違いな事もあったので最後らへんまで飛ばす事をオススメします…
というのも、テックキャンプでの最終課題「商品出品機能」にて、
単体テストコードを実装するという事で、今までそんな詰まったことがなかったので
鼻をほじりながら作業をしていたのですが、めちゃくちゃにやられました。
得意の検索をしまくっても全くこれについての有力な情報がなかったので
今後困るであろう方たちの為に、自分が検索しまくったワードを下記に並べておきますね
(あとはここでハマったエラー群をもうがっつり貼っときます)
ーーー
Item
商品出品機能
できる時
全て問題の無い状態であれば登録に成功する (FAILED – 1)
できない時
○○だと出来ない
Failures:
1) Item 商品出品機能 できる時 全て問題の無い状態であれば登録に成功する
Failure/Error: expect(@item).to be_valid
expected #<Item id: nil, item_name: “あああ”, description: “test”, category_id: 2, condition_id: 2, shipping_charge_id: 2, prefecture_id: 2, shipping_time_id: 2, price: 5000, user_id: nil, created_at: nil, updated_at: nil> to be valid, but got errors: User can’t be blank
# ./spec/models/item_spec.rb:13:in `block (4 levels) in <top (required)>’
Finished in 0.16702 seconds (files took 0.89731 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/models/item_spec.rb:12 # Item 商品出品機能 できる時 全て問題の無い状態であれば登録に成功する
ーーー
テストコード 単体 Rails できない
ActiveHash FactoryBot 連携方法
ActiveHash ファクトリーBOT エラー 解決できない
factory :item do
item_name { "あああ" }
description {'test'}
price {5000}
category_id {2}
condition_id {2}
shipping_charge_id {2}
prefecture_id {2}
shipping_time_id {2}
ーーー
association :user テストコード 単体 エラー できない
Active Storage FactoryBot 成功しない 連携方法
ーーーーーーーーーー
さてさて、長くなりましたが、要は上での検索単語、自分が何をしたかったのか?どこにあたりをつけていたのか?です。
最終課題での商品出品機能には今までのテスト(ユーザー等)では違う要素がいくつかあります。
1、ActiveHashを使用している
2、Active Storageを使用している
3、ユーザー情報と連携している(user_id)
テストコードを実装する際、とりあえず「全て成功している状態」を作るのが第一です。
でなければその後に行う「全て成功したものから削ぎ落として失敗を作る」事ができないからです。
自分は今回、その部分でひっかかりました。
もちろん、手動でのDB登録はこの時点では成功しています。
でもテストコードで成功しない・・・上の3つを切り分けたいがどれがどれでどこから手をつけて良いのかわかりません・・・・
という事で、ここからはうだうだ書きません、上記の問題だと思っていた成功例を書きます。
1、ActiveHashについて
category_id {2}
condition_id {2}
shipping_charge_id {2}
prefecture_id {2}
shipping_time_id {2}
みたいなのでOKです(カラム名は恐らく似通ってますが個々人で違うはずなので)
少し解説すれば、単純にActiveHashはIDでカテゴリ選択を行って判別してますので、それを入れてあげれば良いだけです。
2、Active Storageについて
以下を記述
after(:build) do |item|
item.image.attach(io: File.open('public/images/timage.png'), filename: 'timage.png')
end
3、ユーザー連携について
association :user と書いて @item = FactoryBot.build(:item)
ではなく
@item = FactoryBot.create(:item) と記述するで解決しました。
ーーー以下はそれでもわからない方向けの補足となります。
まずはコード構成ですが
/spec/models/item_spec.rb
require 'rails_helper'
RSpec.describe Item, type: :model do
before do
@item = FactoryBot.create(:item)
end
describe '商品出品機能' do
context 'できる時' do
it '全て問題の無い状態であれば登録に成功する' do
expect(@item).to be_valid
end
end
context 'できない時' do
it '○○だと出来ない' do
end
end
end
end
/spec/factories/items.rb
FactoryBot.define do
factory :item do
item_name { "あああ" }
description {'test'}
price {5000}
category_id {2}
condition_id {2}
shipping_charge_id {2}
prefecture_id {2}
shipping_time_id {2}
association :user
after(:build) do |item|
item.image.attach(io: File.open('public/images/timage.png'), filename: 'timage.png')
end
end
end
これで一旦は通ります。
もっと補足というかアウトプット
今回の問題はbuildか?createか?というところが問題だったようです。
そもそも最初に検索ワードを羅列しましたが、実はすぐに
「user_idってどうすんねん、user_id{1}とかでもブランクになるしわからん・・」
となりましたが、教材にその部分の答えはあるのです。
/spec/factories/items.rb に association :user を入れろ、というものです。
ここまでは恐らく誰でもたどり着くのですが
この後が問題で、教材にはずーーとbuildと使われているのです。
だから問題だとすら思いませんでした。
エラーに直面したその日は顔真っ赤でActiveHashか???これのせいか?なんでだ?画像か?ユーザー連携か????
となってましたが次の日冷静にエラーを見れば
expected #<Item id: nil, item_name: “あああ”, description: “test”, category_id: 2, condition_id: 2, shipping_charge_id: 2, prefecture_id: 2, shipping_time_id: 2, price: 5000, user_id: nil, created_at: nil, updated_at: nil> to be valid, but got errors: User must exist, User can’t be blank
expected #<Item id: nil, item_name: “あああ”, description: “test”, category_id: 2, condition_id: 2, shipping_charge_id: 2, prefecture_id: 2, shipping_time_id: 2, price: 5000, user_id: nil, created_at: nil, updated_at: nil>
「ここまではデータとしてそれぞれ把握しましたよ」
to be valid, but got errors: User must exist, User can’t be blank
「全てマッチしませんね、なぜならuser_idが空だからです。と言われてます。」
その後、ふと目に止まったどっかの記事で
「必ずbuildではなくcreateにして下さい」という文面
その記事は全くの別問題の解決方法が書いてあったのでスルーしてましたが
まさかと思い自分のところを変えたらそのまま通りました・・・・長かった・・・
一応ですが、テストコードに於けるbuildとcreateの違いは
「DBにアクセスするかしないか?」だそうです、buildはアクセスしない為、速度が早いんだとか
今回、DBにアクセスする必要がある理由は、一度DBへ「user_idってそもそもどんな情報もってんの?」とカラムの確認をしにいっているようです。
その後、多分ですがこの前に行ったuserのテストコードへアクセスしています。
そのテストコードでは更にuserテストが実行され、全ての情報が問題ないと確認された後Itemに戻ってきます。
ここまでの情報が無いとuser_idはクリアできないんですねー
「ItemDBのuser_idカラムが数値で埋まってりゃいいんだろ!」とはいかないわけです。
ということで、長々となりましたが、誰かの役に立てば幸いです。
2021/08/10 追記 超恥ずかしい
上で色々言ってましたが、buildでもテストコードが通りました・・・
メンターさんから指摘がありましたが
バリデーションの段階でuesr_idを入れていた為、失敗していたようです。
それを消したあとに、buildに変更したところ成功しましたので、これも追記しておきます。
createじゃなくてbuildにした理由ですが
createだとテストする度にテストした結果がDBに書き込まれます。
どんどん無駄データが増えてしまうわけですね
今回バリデーションをuse_idにかけていたので、ユーザーデータまでテストする必要がありましたが
そこにバリデーションをかけていなければ、ユーザーデータまでテスト参照しなくて良いわけですね
あと、地味にコントローラーのnewの部分を
:newに変更したことも大きいですね
まだまだ、学ばなければいけないことが多そうです。