【Ruby on Rails】to be valid, but got errors: User can’t be blank 【RSpec】が解決できなかった件

今回私、こちらのエラーで死ぬほどひっかかりました。

※途中長々と書いてますが見当違いな事もあったので最後らへんまで飛ばす事をオススメします…

というのも、テックキャンプでの最終課題「商品出品機能」にて、

単体テストコードを実装するという事で、今までそんな詰まったことがなかったので

鼻をほじりながら作業をしていたのですが、めちゃくちゃにやられました。

得意の検索をしまくっても全くこれについての有力な情報がなかったので

今後困るであろう方たちの為に、自分が検索しまくったワードを下記に並べておきますね

(あとはここでハマったエラー群をもうがっつり貼っときます)

ーーー

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に変更したことも大きいですね

まだまだ、学ばなければいけないことが多そうです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA