LoginSignup
12
12

More than 3 years have passed since last update.

関連する子モデルの情報を含んだフォームのバリデーションテスト(Rspec)

Last updated at Posted at 2020-03-22

はじめに

スクールでフリマアプリ開発の際、出品フォームのバリデーションテストを担当しました。
その時、出品画像のバリデーションテストで苦労したので、復習も兼ねて記事に残そうと思います。

今回のケース

0a9433558f4c9427fbc2f041c4edba3c.png

  • 商品名や商品説明を保存するテーブル(productsテーブル)と、出品画像を保存するテーブル(imagesテーブル)が分かれて存在している。
  • アソシエーションは、has_many :images と、belongs_to :product の関係である。
  • 1つのフォームから、複数のテーブルにデータを送信して保存する実装をしている。
  • Productモデルに、validates :images , presence: true を記載しており、画像が無いと商品を出品できないようになっている。

問題なのは、画像が無いと出品ができないため、
フォーム上に画像がアップロードされている状態を、テストで再現しなくてはいけません。
このような場合、どうやってテストすればいいのでしょか?

それについて記述していきます。

テストの方法

まずはfactory_botを使って、imageの情報を含まないProductモデルのインスタンスを準備してみます。

spec/factories/products.rb
FactoryBot.define do
  factory :product do  #画像なしバージョン
    id                {"1"}
    name              {"商品名"}
    detail            {"詳細"}
    category_id       {"686"}
    which_postage     {"1"}
    prefecture        {"1"}
    shipping_date     {"1"}
    price             {"1000"}
    size
    condition
    sending_method
    user
  end
end

今回注目して欲しいのはimageについてなので、それ以外の値については気にしなくて大丈夫です。

この状態で、とりあえず下記のテストを実行してみます。

spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "全ての必須項目が入力されている場合出品できる" do
      product = FactoryBot.build(:product)
      expect(product).to be_valid
    end
  end
end
ターミナル
Product
  #create
    全ての必須項目が入力されている場合出品できる (FAILED - 1)

Failures:

  1) Product#create 全ての必須項目が入力されている場合出品できる
     Failure/Error: expect(product).to be_valid
       expected #<Product id: 1, name: "商品名", detail: "詳細", shipping_date: 1, price: 1000, which_postage: 1, delivery_status: "出品中", prefecture: 1, created_at: nil, updated_at: nil, brand_id: nil, user_id: 1, size_id: 1, condition_id: 1, sending_method_id: 1, buyer_id: nil, category_id: 686> to be valid, but got errors: 商品画像を入力してください
     # ./spec/models/product_spec.rb:154:in `block (3 levels) in <top (required)>'

Finished in 0.63978 seconds (files took 5.52 seconds to load)
1 example, 1 failure

当然ですが、「商品画像を入力してください」と言われ、テストは失敗します。
product.imagesの中身が空っぽな訳です。
なんとかしてproduct.imagesへ画像の情報を入れてあげなければいけません。

その為に、imageのfactoryをproductとは別に準備します。

spec/factories/images.rb
FactoryBot.define do

  factory :image do
    image   {File.open("#{Rails.root}/spec/fixtures/test_image.png")}
    product
  end

end

imageの準備ができたら、spec/factories/products.rb を以下のように編集します。

spec/factories/products.rb
FactoryBot.define do
  factory :product do  #画像ありバージョン
    id                {"1"}
    name              {"商品名"}
    detail            {"詳細"}
    category_id       {"686"}
    which_postage     {"1"}
    prefecture        {"1"}
    shipping_date     {"1"}
    price             {"1000"}
    size
    condition
    sending_method
    user
    after(:build) do |product|                           #追記
      product.images << build(:image, product: product)  #追記
    end                                                  #追記
  end
end

3行だけ追記しました。

after(:build) do |product|
  product.images << build(:image, product: product)
end

after(:build) do 〜 end の部分で、productのインスタンスが作成された直後に、imageのインスタンスを作成して、product.imagesの中にimageインスタンスの情報を入れてあげています。

これでフォーム上に画像がアップロードされた状態を再現することができました!
もう一度テストを実行してみます。

spec/models/product_spec.rb
require 'rails_helper'
describe Product do
  describe '#create' do
    it "全ての必須項目が入力されている場合出品できる" do
      product = FactoryBot.build(:product)
      expect(product).to be_valid
    end
  end
end
ターミナル
Product
  #create
    全ての必須項目が入力されている場合出品できる

Finished in 0.54654 seconds (files took 5.16 seconds to load)
1 example, 0 failures

今度は成功ですね!!

画像の枚数を変更したい場合

after(:build) do |product|
  product.images << build_list(:image, 10, product: product) #buildをbuild_listへ変更
end

build を build_listメソッドにすることで、画像の枚数も操作することができます。
上記の場合は、build_listの第2引数に10を渡している為、画像が10枚アップロードされた状態を再現しています。
境界値などをテストする場合は、build_listを使うといいと思います。

所感

テストに関しては、検索しても情報が少なく、苦労しました。
この記事が少しでも誰かの役に立てば嬉しいです。
初投稿ですので、間違い等ございましたら、ご指摘いただけると幸いです。
ここまで読んでいただき、ありがとうございました!!

参考にさせていただいた記事

accepts_nested_attributes_forで作成するpresence:trueを付けた子モデルに対するモデルのバリデーションチェック~RSpec~
FactoryBotとCarrierWaveを使ってRSpecに画像ファイルアップロードのテストを通す

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12