🐘

【Rails】Factorybot の trait, transient, evaluator

2024/06/04に公開

概要

Rails でテストデータを作成するときに FactoryBot を使う現場は多いと思います。
今回は FactoryBot で頻出する trait, transient, evaluator について整理していきます。
https://github.com/thoughtbot/factory_bot

trait

trait はファクトリー(テストデータのテンプレートを定義するもの)に属性や振る舞いをグループ化して定義することができます。
また、再利用可能なパーツに分けることができ、コードの重複を防ぐことができます。

FactoryBot.define do
  factory :user do
    name { "MyString" }
    age { 20 }

    trait :name_is_john do
      name { "John" }
    end
  end
end
# デフォルト
user = create(:user)
user.name
=> "MyString"

# trait で定義した name_is_john を使用
user = create(:user, :name_is_john)
user.name
=> "John"

また、関連テーブルのレコードも一緒に作成することもできます。
例えば UserArticle が 1対N のような関係のとき、書くことができます。

FactoryBot.define do
  factory :user do
    name { "MyString" }
    age { 20 }

    # article を2つ作成
    trait :with_two_articles do
      after(:create) do |user|
        create_list(:article, 2, user: user)
      end
    end
  end
end
user = create(:user, :with_two_articles)
user.articles.count
=> 2

transient

transient は一時的な属性を定義するときに使用できます。
主に、ファクトリー内でデータを生成するための補助的な情報を提供するために使用されます。

FactoryBot.define do
  factory :user do
    name { "MyString#{"(admin)" if is_admin}" }
    age { 20 }

    transient do
      is_admin { false }
    end
  end
end
user = create(:user, is_admin: true)
user.name
=> "MyString(admin)"

user = create(:user, is_admin: false)
user.name
user.name
=> "MyString"

上記のように、任意の属性 id_admin を定義することで、データ生成時に任意の値をもたせることで、
柔軟なデータ作成ができるようになります。

evaluator

evaluatortransient で定義した属性を、after(:create)などのコールバック内で取得するのに使用します。

先程のように、 UserArticle が 1対N の場合、以下のコードが存在するとします。

FactoryBot.define do
  factory :user do
    name { "MyString" }
    age { 20 }

    transient do
      has_articles_count { 3 }
    end

    after(:create) do |user, evaluator|
      create_list(:article, evaluator.has_articles_count, user: user)
    end
  end
end

これで、has_articles_count に作成したい記事数を指定してあげると、
User の作成と同時に Article を指定した数だけ作成してくれます。

user = create(:user, has_articles_count: 5)
user.articles.count
=> 5

何も指定しないと、デフォルトで指定している3つが作成されます。

user = create(:user)
user.articles.count
=> 3

まとめ

役割 使い時
trait ファクトリーに属性や振る舞いをグループ化して定義することができる。 決まった属性のテストデータを作成したいとき。
transient 一時的な属性を定義するときに使用できる。 作成するテストデータの値を柔軟に変更したいとき。
evaluator transient で定義した属性を、after(:create)などのコールバック内で取得するのに使用する。 コールバック内で transient で定義した属性の値を使用したいとき。

Discussion