Factory Botのデフォルト値をランダムにするか固定値にするか

Factory Botで定義した値をランダム値にするか固定値にするか悩んでいたが、willnet/rspec-style-guide: 可読性の高いテストコードを書くためのお作法集 の説明がわかりやすかったので、コード例を抜粋しつつ以下にまとめる


アカウントが有効化されているかどうかを表すactiveカラムがあったときに、factoryを固定値で定義するとこんなかんじ

FactoryBot.define do
  factory :user do
    name { 'willnet' }
    active { true }
  end
end
RSpec.describe User, type: :model do
  describe '#send_message' do
    let!(:sender) { create :user, name: 'maeshima' }
    let!(:receiver) { create :user, name: 'kamiya' }

    it 'メッセージが正しく送られること' do
      expect { sender.send_message(receiver: receiver, body: 'hello!') }
        .to change { Message.count }.by(1)
    end
  end
end

これだとUser#activeがtrueであることが隠蔽されてしまっている

また、activeがfalseのときの振る舞いを明示できない

改善例がこちら

FactoryBot.define do
  factory :user do
    sequence(:name) { |i| "test#{i}" }
    active { [true, false].sample }
  end
end
RSpec.describe User, type: :model do
  describe '#send_message' do
    let!(:sender) { create :user }
    let!(:receiver) { create :user }

    it 'メッセージが正しく送られること' do
      expect { sender.send_message(receiver: receiver, body: 'hello!') }
        .to change { Message.count }.by(1)
    end
  end
end

activeの値をランダムにしており、これだとテストがactiveの値に依存しないことを明示できる

もしUser#activeのいずれかの値に依存するロジックに書き換わったのだとしたら、ランダムであることで特定の条件下でテストが落ちて気づくことができる

大事なのは「テストに依存している値をすべてテスト中に明示している」ということなので、それが守られているのであればFactoryBotのデフォルト値を固定にしてもかまわない。

仮にほとんどのテストがactiveの値がtrueで事足りる場合でも、各letで active: true とするか activeなユーザーのtraitを作るのが良いかもしれない