グロースエンジニアのブログ

プログラミングとウェブ解析をやっています。Googleアナリティクス好きなRuby on Rails エンジニアです!

factory_bot 再入門

Railsを始めてはや数年、最初のころに factory_girl の勉強をして、それ以来必要に合わせてちょっと調べて終わりって感じで過ごしてきた。

最近Railsを始めた人に、「Railsチュートリアルとかeveryday railsとかやって、開発してるプロダクト見ると書き方が違ってる」って話を聞き、そう言えばその辺の知識って古いまま(と言っても2,3年前?)更新されてないな...と思って読み直すと結構知らなかったことが多かった。

なので、ざっとまとめてみる。

まとめる内容は全部ここに書いてるので、こちらを見るとよいかと!

github.com

attributes_for

これも地味に知らなかった。

# Returns a hash of attributes that can be used to build a User instance
attrs = attributes_for(:user)

Aliases

Factory に alias が付けれる

factory :user, aliases: [:author, :commenter] do
  first_name    "John"
  last_name     "Doe"
  date_of_birth { 18.years.ago }
end

factory :post do
  author
  # instead of
  # association :author, factory: :user
  title "How to read a book effectively"
  body  "There are five steps involved."
end

factory :comment do
  commenter
  # instead of
  # association :commenter, factory: :user
  body "Great article!"
end

はい、知りませんでした...

ポリモーフィックとか使ってるのをちょいちょい見るので、これ使うとわかりやすくなるかな〜とか思ったり。

Transient Attributes

ちょいちょい見て、わかってるようで分かってなかった。 

この例はわかりやすくて、理解できた。

factory :user do
  transient do
    rockstar true
    upcased  false
  end

  name  { "John Doe#{" - Rockstar" if rockstar}" }
  email { "#{name.downcase}@example.com" }

  after(:create) do |user, evaluator|
    user.name.upcase! if evaluator.upcased
  end
end

create(:user, upcased: true).name
#=> "JOHN DOE - ROCKSTAR"

Inheritance

いつも trait を使ってたけど、こういうのもあるのか。一回で書けるって意味ではいいのかもな。

factory :post do
  title "A title"

  factory :approved_post do
    approved true
  end
end

approved_post = create(:approved_post)
approved_post.title    # => "A title"
approved_post.approved # => true
factory :post do
  title "A title"
end

factory :approved_post, parent: :post do
  approved true
end

Associations

alias を使えばいいけど、こういう書き方もある。

factory :post do
  # ...
  association :author, factory: :user, last_name: "Writely"
end

Sequences

こうやって別個に定義できるってのは知らなかったな。

# Defines a new sequence
FactoryBot.define do
  sequence :email do |n|
    "person#{n}@example.com"
  end
end

generate :email
# => "person1@example.com"

generate :email
# => "person2@example.com"
factory :invite do
  invitee { generate(:email) }
end
factory :user do
  email # Same as `email { generate(:email) }`
end

んで、aliasも定義できる

factory :user do
  sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end

# will increase value counter for :email which is shared by :sender and :receiver
generate(:sender)

Linting Factories

Lint ってよくあるけど、factory_bot でもあるって知らなかった。

「うるせー!」 って言いたくなることもあるけど(笑)

FactoryBot.lint

まとめ

これ以外にもいろいろ紹介されてたけど、基本的なところはこんなところかなと!

gemも定期的に見て、機能とか確認していかないとダメだなというのが今回の気付き!

github.com