LoginSignup
1
2

More than 3 years have passed since last update.

子レコードがないと保存ができない親モデルの連動テストを作ろうとして四苦八苦した話。

Last updated at Posted at 2020-05-10

前回までのあらすじ

親モデルと子モデルの単体テストはできたのですが、親モデルが子モデルのレコードがなくても保存できてしまっていることに気付きました。
今回は子レコードが保存できない場合に親レコードで保存できないようにバリデーションを組もうとして四苦八苦したことを記事に残していこうと思います。

下準備

前回記事とか前々回記事とかその前の記事とかの下準備を参照ください。

今回のテスト環境の準備

バリデーションの追加

validates :images,presence: trueを追記します。

app/models/items.rb
(中略)
  validates :title,:images,presence: true #:imagesを追記

インスタンスの作成

いくつかのパターンを想定してインスタンスを作成しました。
images.rbのインスタンスの内訳は以下です。
:imageレコードの保存が期待できるインスタンス
:without_imageimageがnullで保存に失敗することが期待されるインスタンス
:with_textimageにテキストファイルを保存しようとして保存に失敗することが期待されるインスタンス

spec/factoreis/image.rb
FactoryBot.define do
  factory :image do
    image    { Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/fixtures/image.jpg'))}
    association :item

    factory :without_image ,class:Image do
      image    {""}
      association :item
    end

    factory :with_text ,class:Image do
      image    { Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/fixtures/image.txt'))}
      association :item
    end
  end
end

items.rbのインスタンスの内訳は以下です。
:item 保存が期待できるインスタンス
:item_without_name nameがnullで保存に失敗することが期待されるインスタンス
:item_without_image :without_imageのインスタンスで子モデルのレコードの保存に失敗して、親レコードも保存に失敗することが期待されるインスタンス
:item_with_text :with_textのインスタンスで子モデルのレコードの保存に失敗して、親レコードも保存に失敗することが期待されるインスタンス

spec/factoreis/items.rb
FactoryBot.define do
  factory :item do
    name    {"test"}
    after(:build) do |item|
      item.images << build(:image,item: item)
    end

    factory  :item_without_name,class:Item do
      name  {""}
    end

    factory :item_without_image,class:Item do
      after(:build) do |item|
        item.images << build(:without_image,item: item)
      end
    end

    factory :item_with_text,class:Item do
      after(:build) do |item|
        item.images << build(:with_text,item: item)
      end
    end
  end
end

テストコードを記述

spec/models/image_spec.rb
require 'rails_helper'

RSpec.describe Image, type: :model do
  describe '#create' do
    let(:image) {build_stubbed(:image)}
    context 'can save' do
      it "is valid with a image,item_id" do
        expect(image).to be_valid
      end
    end
    context 'can not save' do
      it "is invalid without a image" do
        expect(build_stubbed(:without_image)).to be_invalid
      end

      it "is invalid with a image with filetype of not image file" do
        expect(build_stubbed(:with_text)).to be_invalid
      end

      it "is invalid without a item_id" do
        image.item_id = ""
        expect(image).to be_invalid
      end
    end
  end
end
spec/models/item_spec.rb
require 'rails_helper'

RSpec.describe Item, type: :model do
  describe '#create' do
    let(:item) {build(:item)}
    context 'can save' do
      it "is valid with a name,images" do
        expect(item).to be_valid
      end
    end

    context 'can not save' do
      it "is invalid without a name" do
        expect(build(:item_without_name)).to be_invalid
      end
      it "is invalid without images" do
        expect(build(:item_without_image)).to be_invalid
      end
      it "is invalid with a file is type of text" do
        expect(build(:item_with_text)).to be_invalid
      end
    end
  end
end

準備ここまで。

テストの実行結果

Terminal.
bundle exec rspec spec/models/

Image
  #create
    can save
      is valid with a image,item_id
    can not save
      is invalid without a image
      is invalid with a image with filetype of not image file
      is invalid without a item_id

Item
  #create
    can save
      is valid with a name,images (FAILED - 1)
    can not save
      is invalid without a name
      is invalid without images
      is invalid with a file is type of text

Failures:

  1) Item#create can save is valid with a title
     Failure/Error: expect(item).to be_valid
       expected #<Item id: nil, title: "test", created_at: nil, updated_at: nil> to be valid, but got errors: Images item can't be blank
     # ./spec/models/item_spec.rb:8:in `block (4 levels) in <top (required)>'

Finished in 1.17 seconds (files took 5.74 seconds to load)
8 examples, 1 failure

Failed examples:

rspec ./spec/models/item_spec.rb:7 # Item#create can save is valid with a name,images

:itemの保存ができずに保存に失敗してしまいました。

原因を探る

imageモデルのvalidates :item_id,presence: trueが原因でitemの保存ができていませんでした。

app/models/image.rb
class Image < ApplicationRecord
(中略)
  validates :image,presence: true  #:item_idを削除
end

ただ、外部キーのitem_idnullでも保存できてしまい、imageモデルのテストが失敗する懸念があります。

結果

Terminal.
bundle exec rspec spec/models/
Image
  #create
    can save
      is valid with a image,item_id
    can not save
      is invalid without a image
      is invalid with a image with filetype of not image file
      is invalid without a item_id

Item
  #create
    can save
      is valid with a name
    can not save
      is invalid without a name
      is invalid without images
      is invalid with a file is type of text

Finished in 1.11 seconds (files took 5.81 seconds to load)
8 examples, 0 failures

杞憂でした。

教訓

親モデルにバリデーションをかければ、子モデルの外部キーのバリデーションは必要ない(多分)

今回は以上です。

1
2
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
1
2