Railsではrails db:seedという便利なコマンドがあり、db/seed.rbにシードデータを入れておけばコマンドを叩いたら勝手にシードデータを作成してくれます。 ただ、1度だけ使うシードデータを作成する場合はいいですが、例えばシードデータを編集して再度実行すると、同じデータが何個も作成されてしまうという問題点もあります。 そこで今回は、便利にシードデータを生成してくれるseed-fuというgemを使って、development,test,productionそれぞれの環境毎にシードデータを作成していきたいと思います。

seed-fuの導入

  1. Gemfileに下記を記述後、bundle install。本番環境でも使いたいgemなので環境指定はしないでください。
# Gemfile

...

gem 'seed-fu'

...
  1. dbディレクトリ配下にfixturesディレクトリを作成

これで準備は完了です。

シードデータの投入

あとはdb/fixtures配下にモデル単位でシードファイルを作成していきます。
例えばCustomerモデルとCustomerに紐づくAddressモデルがあった場合、以下のように書きます。

# db/fixtures/customers.rb

Customer.seed do |s|
  s.id = 1
  s.name = 'テストユーザー'
  s.email = 'public@example.com'
end
# db/fixtures/addresses.rb

Address.seed do |s|
  s.id = 1
  s.prefecture = '東京都'
  s.city = '渋谷区'
  s.customer_id = 1
end

上記のファイルを作成したら、rails db:seed_fuでDBにデータが投入されます。 がしかし、エラーが発生してしまいました。

== Seed from /Users/miwa/study/practice-app/db/fixtures/addresses.rb
 - Address {:id=>1, :prefecture=>"東京都", :city=>"渋谷区", :customer_id=>1}
rake aborted!
ActiveRecord::InvalidForeignKey: SQLite3::ConstraintException: FOREIGN KEY constraint failed
(eval):1:in `block (2 levels) in run_file'

Caused by:
SQLite3::ConstraintException: FOREIGN KEY constraint failed
(eval):1:in `block (2 levels) in run_file'
Tasks: TOP => db:seed_fu
(See full trace by running task with --trace)

Customerに何もデータが入っていないのにAddressがCustomerを参照しようとしてしまっているからですね。
こういう場合は実行するシードファイルの順番を制御する必要があります。

実行するシードデータの順番を制御したい時

この問題は実はとても簡単に解決出来ます!
seed-fuはfixturesディレクトリの中のファイルを上から順に実行するので、ファイル名にprefixをつけてあげたらOKです!
先ほどの状態だと、customer.rbよりもaddress.rbの方が上になっているので、address.rbが先に実行されてエラーを起こしています。
これらのファイル名を下記のように書き換えます。

customer.rb → 01_customer.rb
address.rb → 02_address.rb

こうすれば01_customer.rbが先頭に来てくれます。再度rails db:seed_fuを実行しましょう。

== Seed from /Users/miwa/study/practice-app/db/fixtures/01_customers.rb
 - Customer {:id=>1, :name=>"テストユーザー", :email=>"public@example.com"}

== Seed from /Users/miwa/study/practice-app/db/fixtures/02_addresses.rb
 - Address {:id=>1, :prefecture=>"東京都", :city=>"渋谷区", :customer_id=>1}

DBにシードデータが投入されました。

環境ごとにシードファイルを分けたい時

この場合はfixturesディレクトリの中に「環境名」ディレクトリを作成し、そこにシードファイルを設置することで実現できます。
ちょうど下記の写真のようなイメージです。
スクリーンショット 2020-07-06 17.32.38

逆にどの環境でも実行したいファイルは、fixtures直下に設置すればOKです。

テスト時にシードデータを生成したい時

例えばrspecを使って自動テストを行う場合、マスタデータなどはFactoryBotとは別で、シードファイルとして持っておいた方がベターです。
その場合、下記のように設定を行います。

  1. テスト用シードファイルを作成
# rspec/initial_data/01_customer.rb

Customer.seed do |s|
  s.id = 1
  s.name = 'テストユーザー'
  s.email = 'public@example.com'
end
# rspec/initial_data/02_addresses.rb

Address.seed do |s|
  s.id = 1
  s.prefecture = '東京都'
  s.city = '渋谷区'
  s.customer_id = 1
end
  1. rspec/spec_helper.rbに下記の様に記載
# rspec/spec_helper.rb

RSpec.configure do |config|

  config.before(:suite) do
    fixture_path = "#{Rails.root}/spec/initial_data"
    SeedFu.seed(fixture_path)
  end

end

これでテストが実行される度に、実行前にシードデータが挿入されます。

まとめ

今回はsee-fuを使って、開発〜テスト〜本番環境でのシードデータ作成方法を解説しました。
開発環境と本番環境はディレクトリを分ければいいだけですが、テスト環境では少し設定を編集しないといけません。
ただ、この設定を一度行ってしまえば同じ設定をする必要が無くなりますので、開発スピードがとても速くなります。
以上、seed-fuの使い方でした!