このエントリーをはてなブックマークに追加

2016年10月26日水曜日

FactoryGirlのsequenceで詰まったメモ

こんにちは、Taroです。
最近Railsを勉強し始め、やっとテストコードを書くようになりました。
FactoryGirlって便利ですよね。
特に連番を作成する際に使用するsequenceがお気に入りです。
しかし先日愛するsequenceに頭を悩まされることになりました。
(当たり前だよ、とは言わないでください。)

sequenceのnって、テスト2週目でも値がリセットされるわけじゃないんだぜ!


まずは状況を理解していただくため、例をあげたいと思います。
例えば下記のようにModelを定義します。

spec/factories/item.rb
FactoryGirl.define do
  factory :item do
    sequence(:name) { |n| "sample#{n}" }
    sequence(:category) { |n|
      n % 4 == 0 ? 4 : n % 4
    }
  end
end
意図としては、名前は「sample1」、「sample2」。。。といった具合に増やします。
ただカテゴリは1〜4の中に収めようとしました。
そしてテストコードを書きます。

spec/models/item_spec.rb
require 'rails_helper'
RSpec.describe Item, type: :model do
  context 'itemのレコードが存在するとき' do   
    before do
      FactoryGirl.create_list(:mst_content_type, 12)
    end
    it 'テスト1' do
      # 省略
    end
    it 'テスト2' do
      # 省略
    end
  end
end
create_listメソッドを使うことで、第2引数の数だけサンプルレコードを作成できます。
(ただし、1つ1つ指定できないことがネックです。)
12レコード作成しておりますが、これは意図した数です。
テストを実行しました。何やら狙った結果が得られません。
どうやらサンプルデータがおかしいということが分かりました。
# テスト1週目
id: 1 name: sample1 category: 1
id: 2 name: sample2 category: 2
...以下10個同様のものが作成された。

# テスト2週目
id: 13 name: sample13 category: 3
id: 14 name: sample14 category: 4
...以下10個同様のものが作成された。
お気づきでしょうか?1週目と2週目でレコードが違うことに。
カテゴリーで絞り込みを行っていると、この辺りで引っかかり、狙った結果を得ることができません。
また、私のように不作法なリレーションのテストもわけが分からないことになります。
(テストコードが悪いのか、それともそもそもテストしたいソースが悪いのか)

いやいや、なんでお前、カテゴリーみたいな範囲のある値にsequence使ってるの?


おっしゃる通りです。。。FactoryGirlにはtraitというグループ分けの機能がありました。

spec/factories/item.rb
FactoryGirl.define do
  factory :item do
    sequence(:name) { |n| "sample#{n}" }
    
    trait category_1 do
      category 1
    end

    trait category_2 do
      category 2
    end

    trait category_3 do
      category 3
    end

    trait category_4 do
      category 4
    end
  end

  factory :item_content_1, traits: [:category_1], parent: :item
  factory :item_content_2, traits: [:category_2], parent: :item
  factory :item_content_3, traits: [:category_3], parent: :item
  factory :item_content_4, traits: [:category_4], parent: :item
end
親を:itemとして、属性を付加します。
下手なことをするよりも、これで3つずつ計12個レコードを作成すればよさそうです。
呼ぶ時は先ほどと同様にcreateやcreate_listでいけます。

spec/models/item_spec.rb
require 'rails_helper'
RSpec.describe Item, type: :model do
  context 'itemのレコードが存在するとき' do   
    before do
          FactoryGirl.create_list(:item_content_1, 3)
          # もしくはこんな感じ(createならばその他の値も設定できるので、融通がききます。)
          3.times { FactoryGirl.create(:item_content_2) }
      end

      # 省略

  end
end
もしもっと良い方法をご存知の方がいらっしゃいましたら教えてください!

余談:sequenceに初期値を設定する


ちなみにsequenceには初期値が設定できます。
といっても簡単で、第2引数に値を設定します。(何も指定しなければ1です。)
# 100番からスタート
sequence(:name, 100) { |n| "sample#{n}" }

0 件のコメント:

コメントを投稿