FactoryBotのtrait、transient、evaluatorについて
概要
Railsのテストでテストデータを作成する際に便利なgemである、FactoryBotにあるtrait、transient、evaluatorの機能について備忘録として書きます。
trait
通常のテストデータの作成とは別に、モデルの属性値の集合を定義できる。共通の属性値を持ったテストデータの作成時、重複を避けることができる。
一例として、以下のようなUserのファクトリを作成します。
FactoryBot.define do factory :user do sequence(:name) { |n| "admin-#{n}" } password { 'password' } password_confirmation { 'password' } role { :admin } trait :writer do sequence(:name) { |n| "writer-#{n}" } role { :writer } end end end
通常の書き方は以下の通りで、nameがadmin-1、roleがadminのデータを作成している。
let(:user) { create(:user) } pry(~)> user => #<User:0x00007fd7e6a9f0e0 id: 1, name: "admin-1", password: "password", password_confirmation: "password", role: "admin", created_at: Tue, 25 May 2021 03:14:48 JST +09:00, updated_at: Tue, 25 May 2021 03:14:48 JST +09:00>
次にtraitを活用したuserは、nameがwriter-1、roleがwriterのデータを作成している。
let(:writer) { create(:user, :writer) } pry(~)> writer => #<User:0x00007fd7f23b4eb8 id: 2, name: "writer-1", password: "password", password_confirmation: "password", role: "writer", created_at: Tue, 25 May 2021 03:15:04 JST +09:00, updated_at: Tue, 25 May 2021 03:15:04 JST +09:00>
transient
テストデータの元となるモデルに定義されてない任意の属性を定義することができる。
一例として、以下のようなUserのファクトリを作成します。
FactoryBot.define do factory :user do transient do state_name { 'user' } end name { "tanaka(#{state_name})" } password { 'password' } password_confirmation { 'password' } trait :admin do state_name { 'admin' } end trait :writer do state_name { 'writer' } end end end
traitのadminを使用した場合、nameがtanaka(admin)のデータを作成している。
let(:admin) { create(:user, :admin) } pry(~)> admin.name => "tanaka(admin)"
traitのwriterを使用した場合、nameがtanaka(writer)のデータを作成している。
let(:writer) { create(:user, :writer) } pry(~)> writer.name => "tanaka(writer)"
evaluator
FactoryBotのcallbackとして渡されている引数。その中身は、FactoryBotで作成されたインスタンスも、transientで定義した属性もハッシュの形で返してくれるオブジェクト。transientで定義した属性を取り出したいときに必要となる引数。
一例として、Articleを作成後、一対多のアソシエーションを結んだAuthorモデルのデータを作成するファクトリを作成します。
FactoryBot.define do factory :article do sequence(:title) { |n| "title-#{n}" } state { :published } end trait :with_author do transient do sequence(:author_name) { |n| "test_author_name_#{n}" } end after(:build) do |article, evaluator| article.author = build(:author, name: evaluator.author_name) end end
with_authorのtraitを使用したデータを作成後、authorメソッドを使用することでAuthorモデルのデータを取得することができる。
let(:article_with_author) { create(:article, :with_author) } pry(~)> article_with_author.author => #<Author:0x00007f9a263a0670 id: nil, name: "test_author_name_1", created_at: nil, updated_at: nil>