今回はRailsのテスト、RSpecのFactoryで用いることのできるtrait(トレイト)について解説したいと思います。traitを使用することで、冗長になりがちな継承のテストコードをすっきり書くことができます。
覚えておいて損はない知識だと思うので、ぜひ使いこなせるようにしましょう。

traitとは
traitとは、FactoryBotで複数のサンプルデータを作成する際に使われるテクニックです。Factoryが複数ある場合、traitを使用することで記述の重複を減らすことができ、可読性を高めたコードにすることができます。
traitの定義方法は以下になります。
FactoryBot.define do
factory :モデル名 do
trait :上記モデルを継承したインスタンスの属性値 do
end
end
end
定義方法のみでは分かりにくいと思うので記述例を挙げてみます。
締め切りが異なる2つのプロジェクト(1週間前が締め切りのプロジェクトと昨日が締め切りのプロジェクト)を作成する場合は、traitを用いて以下のようにスマートに記述することができます。
FactoroyBot.define do
factory :project do
# 1週間前が締め切り
sequence(:name) { |n| "Test Project #{n}" }
description "Sample project for testing purposes"
due_on 1.week.from_now
association :owner
# 昨日が締め切り
trait :due_yesterday do
due_on 1.day.ago
end
end
end
sequenceは、ユニーク(unique:trueとuniqueness: true)なバリデーションを持ったカラムに使用することができます。sequenceに設定した「n」が、データ作成毎に1ずつ増えていくことで重複を避けています。
上記コードでは以下のようになります。
1回目:”Test Project 1″
2回目:”Test Project 2″
3回目:”Test Project 3″
では上記のようにtraitを使わなかった場合、どのくらいコードが冗長・重複してしまうか次の項目で見ていきましょう。
traitを使うまでの過程
締め切りが異なる2つのプロジェクトを作成したい場合、以下のようにインスタンス名を変更し、クラス名を渡すことで実装ができます。
FactoroyBot.define do
factory :project do
sequence(:name) { |n| "Test Project #{n}" }
description "Sample project for testing purposes"
due_on 1.week.from_now
association :owner
end
# 昨日が締め切りのプロジェクト
factroy :project_due_yesterday, class: Project do
sequence(:name) { |n| "Test Project #{n}" }
description "Sample project for testing purposes"
due_on 1.day.ago
association :owner
end
end
ただ、上記の記述では重複箇所が多くDRYなコードになっていません。
そこでまずは継承の機能を使い、重複箇所を減らしていきます。
継承を使うことで「class: Project」と重複した記述が不要になり、以下のようなコードになります。親Factoryであるprojectを継承したproject_due_yesterdayが入れ子構造になっていますね。
FactoroyBot.define do
factory :project do
sequence(:name) { |n| "Test Project #{n}" }
description "Sample project for testing purposes"
due_on 1.week.from_now
association :owner
# 昨日が締め切りのプロジェクト
factroy :project_due_yesterday do
due_on 1.day.ago
end
end
end
構造だけを抜き出すと以下のようになります。
factory :project
factory :project_due_yestreday
ここからtraitを用いてさらにリファクタリングしていきます。
traitを使用することで継承するモデル名を省略することができ、以下のようなコードを書くことができます。そして、specファイルでtraitを呼び出すには引数として渡す必要があります。
FactoroyBot.define do
factory :project do
sequence(:name) { |n| "Test Project #{n}" }
description "Sample project for testing purposes"
due_on 1.week.from_now
association :owner
# 昨日が締め切り
trait :due_yesterday do
due_on 1.day.ago
end
end
end
describe "latestatus" do
# 締切日が過ぎていれば遅延していること
it "is late when the due date is past today" do
# 以下のように引数としてtraitを渡す必要がある
project = FactoryBot.create(:project, :due_yesterday)
expect(project).to be_late
end
end
まとめ
- traitとは、FactoryBotで複数データを作成する際に使われるテクニックのこと
- traitを使用することで、記述の重複を減らし可読性を高めることができる
- traitをspecファイルで呼び出すには、引数として渡す必要がある
参考
今回はRailsのテスト、RSpecのFactoryで用いることのできるtrait(トレイト)について解説しました。継承やtraitの機能を使いこなし、DRYなコードを心がけていきましょう。