フリマアプリの出品テストが上手く書けない人へ


初投稿です!
どうか優しい目で見てあげて下さい。お願いします!

さてさて、今回はテストコードの初歩的な事について備忘録的に書き残していこうかと思います。

フリマアプリを開発上で、商品(product)の出品時の

「必須項目が全て入力してある際は登録できる」

といったテストコードを書こうと思います!


今回登録する中身はこちら↓

productsテーブル

カラム名 オプション
name string null: false
price integer null: false
description(商品説明) string null: false
status(商品の状態) string null: false
size string null: false
judgment integer
days(発送までの日にち) string null: false
cost(配送料負担者) string null: false
prefecture_id integer null: false
category_id integer null: false, foreign_key: true
brand_id integer foreign_key: true
user_id integer null: false, foreign_key: true

テーブルから
name,
price,
description,
status,
size,
days,
cost,
prefecture_id,
category_id,
user_id
のカラムの値があれば登録できそう( ^ω^ )

というわけで、今回はFactoryBotを用いて、下記の様にテストコードを書きました!!

spec/factories/product.rb
FactoryBot.define do

  factory :product do  #値は適当です
    name              {"tomato"}
    price             {111}
    description       {"aaa"}
    status            {"aaa"}
    size              {"aaa"}
    days              {"aaa"}
    cost              {"aaa"}
    prefecture_id     {1}
    category_id       {1}
    user_id           {1}
  end

end
spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
      product = FactoryBot.build(:product)
      expect(product).to be_valid
    end
  end
end

さてこれで、【bundle exec rspec】っと( ^ω^ )

 結果

ターミナル
Product
  #create
    is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id (FAILED - 1)

Failures:

  1) Product#create is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id
     Failure/Error: expect(product).to be_valid
       expected #<Product id: nil, name: "abe", price: 111, description: "aaa", status: "aaa", size: "aaa", judgment: nil, days: "aaa", cost: "aaa", prefecture_id: 1, category_id: 1, brand_id: nil, user_id: 1, created_at: nil, updated_at: nil> to be valid, but got errors: Userを入力してください, Categoryを入力してください
     # ./spec/models/product_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.18668 seconds (files took 3.15 seconds to load)
1 example, 1 failure

失敗( ´ ▽ ` )

エラー分を見てみると、
「UserとCategoryってどこのどいつ誰だよ!そんなのどこにもいねぇぞ!」
と怒られてます。

それもそのはず…
user_idとcategory_idを外部キーで指定しているのにその先が居ないからね…。( ˊ̱˂˃ˋ̱ )

そこで、
FactoryBotにUserとCategoryも追記します!!
まずここでUserとCategoryのテーブルを確認↓

Usersテーブル

カラム名 オプション
nickname string null: false
email string null: false
encrypted_password string null: false
user_image (プロフィール画像) string
introduction (自己紹介) text
family_name (姓) string null: false
first_name (名) string null: false
family_name_kana (セイ) string null: false
first_name_kana (メイ) string null: false
birth_day (生年月日) date null: false

Categoriesテーブル

カラム名 オプション
name string null: false
ancestry string

なので、FactoryBotに以下の様に追記!

spec/factories/product.rb
# ----------追記部分ここから--------------
FactoryBot.define do
  factory :user do  #値は適当です
    nickname          {"sasa"}
    email             {"[email protected]"}
    password          {"00000000"}  #登録の際に必要なので追記!
    encrypted_password{"00000000"}
    family_name       {"sasaki"}
    first_name        {"goro"}
    family_name_kana  {"sasaki"}
    first_name_kana   {"goro"}
    birth_day         {"1990-08-24"}
  end

  factory :category do  #値は適当です
    name              {"aaa"}
  end
# ----------追記部分ここまで--------------

  factory :product do  #値は適当です
    name              {"tomato"}
    price             {111}
    description       {"aaa"}
    status            {"aaa"}
    size              {"aaa"}
    days              {"aaa"}
    cost              {"aaa"}
    prefecture_id     {1}
    category_id       {1}
    user_id           {1}
  end

end

そしてuserとcategoryはこのテスト内で一旦保存されないと、
また「UserとCategoryってどこのどいつ誰だよ!そんなのどこにもいねぇぞ!」
と怒られてしまうので、
buildではなくcreateを使って行きます!!

spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
      user = create(:user)
      category = create(:category)
      product = FactoryBot.build(:product)
      expect(product).to be_valid
    end
  end
end

これでいける!!
【bundle exec rspec】っと( ^ω^ )

 結果

ターミナル
Product
  #create
    is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id (FAILED - 1)

Failures:

  1) Product#create is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id
     Failure/Error: expect(product).to be_valid
       expected #<Product id: nil, name: "abe", price: 111, description: "aaa", status: "aaa", size: "aaa", judgment: nil, days: "aaa", cost: "aaa", prefecture_id: 1, category_id: 1, brand_id: nil, user_id: 1, created_at: nil, updated_at: nil> to be valid, but got errors: Userを入力してください, Categoryを入力してください
     # ./spec/models/product_spec.rb:8:in `block (3 levels) in <top (required)>'

Finished in 0.17322 seconds (files took 2.77 seconds to load)
1 example, 1 failure

…えっ?_:(´ཀ`」 ∠):

同じエラーでとる…何故…??

もしかして、userもcategoryもcreateできてない?
と思い、【binding.pry】を【category = create(:category)】の直下に記入し、
確かめてみると、、、

Product
  #create

From: /Users/hoge/Desktop/GitHub/hogehoge/spec/models/product_spec.rb @ line 7 :

     2: describe Product do
     3:   describe '#create' do
     4:     it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
     5:       user = create(:user)
     6:       category = create(:category)
 =>  7:       binding.pry
     8:       product = build(:product)
     9:       expect(product).to be_valid
    10:     end
    11: 
    12:     # it "is invalid without a name" do

[1] pry(#<RSpec::ExampleGroups::Product::Create>)> user
=> #<User id: 15, nickname: "sato", email: "[email protected]", user_image: nil, introduction: nil, family_name: "sato", first_name: "kenta", family_name_kana: "sato", first_name_kana: "kenta", birth_day: "1990-08-24", created_at: "2020-03-21 05:17:39", updated_at: "2020-03-21 05:17:39">
[2] pry(#<RSpec::ExampleGroups::Product::Create>)> category
=> #<Category:0x00007fe4caab5138 id: 7, name: "aaa", ancestry: nil, created_at: Sat, 21 Mar 2020 05:17:39 UTC +00:00, updated_at: Sat, 21 Mar 2020 05:17:39 UTC +00:00>
[3] pry(#<RSpec::ExampleGroups::Product::Create>)> 

なんてことない、userとcategoryのidが違っていただけでした。( ´ ▽ ` )
そりゃ常にidが1な訳ないよね。。。

と言うことでさらにテストコードを下記の様に修正!!

spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id" do
      user = create(:user)
      category = create(:category)
    #修正点!! user_id: user[:id], category_id: category[:id]を追記、これでテスト時に保存されたuserとcategoryのidが呼び出され、上書きされる
      product = FactoryBot.build(:product, user_id: user[:id], category_id: category[:id])
      expect(product).to be_valid
    end
  end
end

これでどうかな?
( ^ω^ )つ【bundle exec rspec】

 結果

ターミナル
Product
  #create
    is valid with a name, price, description, status, size, days, cost, prefecture_id, category_id, user_id

Finished in 0.07648 seconds (files took 2.63 seconds to load)
1 example, 0 failures

無事テスト成功!!

やったぜ!✌︎('ω'✌︎ )( ✌︎'ω')✌︎

まとめ

テスト内容は単純でも、
アプリごとにテーブルなどの制約(NotNullや、外部キーなど)も異なるので、
そこに気をつけてテストコードを書いていかないと、
私の様に、テストコードの沼にハマる事になります。

この記事が少しでも初学者の役に立ちます様に。

ここまで読んでくれて
ありがとう。
また次の記事でお会いしましょう!!♪( ´θ`)ノそれでは