notebook

都内でWEB系エンジニアやってます。

Rails5+複合主キー+FactoryBotでテストデータを作成する

Rails5+複合主キーでやっていくにはcomposite_primarykeyが必要ですが

テストコードを書く際にハマったのでその時のメモを残しておきます

FactoryBotでテストデータを作成する際の話です

composite_prrimarykeyを使ってreportsテーブルを複合主キーで扱うことにしています

  • migrate
  def change
    create_table :reports, primary_key: ['id', 'date'] do |t|
      t.bigint :id, null: false, auto_increment: true
      t.date :date, null: false
      t.integer :hoge, null: true
      t.timestamps
    end
  end
  • app/models/report.rb

modelで明示的に主キーをid,dateと指定しています

class Report < ApplicationRecord
  self.primary_keys = :id, :date
end
  • spec/factories/report.rb

テスト用のデータ部分を記述します

FactoryBot.define do
  factory :report do
    sequence(:id) {|n| n}
    sequence(:date) {|n| Date.today.subtract }
    ....
    ....
    ....
    ....
  end
end
  • spec/models/report_spec.rb

簡単なテストコードを書いてみます

RSpec.describe Report, type: :model do
  describe 'sample' do
    before(:each) do
      FactoryBot.create_list(:report, 2)
    end

    context 'hoge' do
      it do
        expect(Report.all.size).to eq(2)
      end
    end
  end
end

実行するとエラーが出ます

Failures:

  1) Report sample hoge
     Failure/Error: let(:row) { FactoryBot.create(:report) }

     ArgumentError:
       Unsupported type: 1

簡単に抜粋だけしましたがこれだと上記エラーが出てきます

ただArgumentError: Unsupported type: しかでてきません

ソース追ってみると該当箇所の主キーの渡し方が違うようです

    def self.parse(value)
      case value
      when Array
        value.to_composite_keys
      when String
        self.new(value.split(ID_SEP))
      else
        raise(ArgumentError, "Unsupported type: #{value}")
      end
    end

このparseはArrayかStringしか受け付けないようになっています

なので配列で渡すか、1,2というふうに区切り文字で区切って渡す方法が用意されているようです

それに対応させて渡してあげれば良いわけですね

今回はidをid,dateで指定しているので同じようにテストデータ作成時もidに対して渡して上げる必要があるようです

後でREADME見てみたら普通にのっていたのでちゃんと読めって話ではあるのですがいつもパーティションはった複合主キーのテーブルに対してはactiverecord-importでしかデータ入れてなかったためちょっとはまりました

なのでこんな感じでデータを作ってあげるようにすれば良い感じです

  • spec/factories/report.rb
  sequence(:id) {|n| [n, Date.today]}

おわり