RSpec

【RSpec初級編】Factoryの継承をスマートに記述できるtrait(トレイト)を使いこなそう!

RSpec

 

今回はRailsのテスト、RSpecのFactoryで用いることのできるtrait(トレイト)について解説したいと思います。traitを使用することで、冗長になりがちな継承のテストコードをすっきり書くことができます。

覚えておいて損はない知識だと思うので、ぜひ使いこなせるようにしましょう。

RSpec
【RSpec初級編】FactoryBotを用いてテストコードを効率化する方法について解説RSpecのテストコードを効率化できるツール「FactoryBot」について初心者向けに解説しています。今後RSpecのサンプルデータを作成する際はFactoryBotを使用するよう心がけていきましょう。...

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″

参照:Everyday Rails

 

では上記のように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
「class: Project」という記述でクラスを渡している点に注意しましょう。

 

ただ、上記の記述では重複箇所が多く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ファイルで呼び出すには、引数として渡す必要がある

参考

Everyday Rails

 

 

今回はRailsのテスト、RSpecのFactoryで用いることのできるtrait(トレイト)について解説しました。継承やtraitの機能を使いこなし、DRYなコードを心がけていきましょう。