Updated: Ruby: Don't build randomness into your factories

Posted . Visible to the public. Auto-destruct in 60 days
  • Added guidance for when random values are unavoidable, recommending FactoryBot sequences and rewinding them before each example
  • Added a “If you must use Faker” section showing how to set a fixed random seed and clear Faker::UniqueGenerator before each test

Changes

  • -Tests are about 100% control over UI interaction and your test scenario. Randomness makes writing tests hard. You will also push tests that are green for you today, but red for a colleague tomorrow.
  • +Tests are about 100% control over UI interaction and your test scenario. Randomness will eventually cause flaky tests that are hard to debug.
  • -That said, please don't do something like this:
  • +
  • +The case against random values
  • +------------------------------
  • +
  • +Avoid setting attributes to random values like this:
  • +
  • +```ruby
  • +factory(:document) do |document|
  • + category { ['foo', 'bar', 'baz'].sample }
  • +end
  • +```
  • +
  • +This makes tests randomly fail when they expect "foo" for some other reason, but an unreleated document in the "foo" category happens to also be on the screen.
  • +
  • +Instead just use the same attribute value every time:
  • ```ruby
  • -Factory(:document) do |document|
  • -document.category { ['foo', 'bar', 'baz'].sample }
  • +factory(:document) do |document|
  • + category 'foo'
  • end
  • ```
  • -Instead do this:
  • -
  • +### If you must use random values
  • +
  • +If you absolutely cannot use the same attribute value for every record, use a [FactoryBot sequence](https://thoughtbot.github.io/factory_bot/ref/sequence.html) and reset if before every example:
  • +
  • +```ruby
  • +factory(:document) do |document|
  • + sequence(:category) { |i| ['foo', 'bar', 'baz'][i % 3] }
  • +end
  • +```
  • +
  • ```ruby
  • -Factory(:document) do |document|
  • -document.category 'foo'
  • +# spec/support/factory_bot.rb
  • +
  • +RSpec.configure do |config|
  • +config.prepend_before do
  • + FactoryBot.rewind_sequences
  • + end
  • end
  • ```
  • +
  • +
  • The case against Faker
  • ----------------------
  • -I even recommend to **not** use libraries like [Faker](https://github.com/faker-ruby/faker). Faker makes awesome sample data, but at the risk of adding random strings to your screen.When your factory requires unique values for something, prefer numbering fixed strings intead:
  • +I even recommend to **not** use libraries like [Faker](https://github.com/faker-ruby/faker). Faker makes awesome sample data, but at the risk of adding random strings to your screen.
  • +
  • +When your factory requires unique values for something, prefer numbering fixed strings intead:
  • ```ruby
  • FactoryBot.define do
  • sequence(:first_name) { |i| "First#{i}" }
  • sequence(:last_name) { |i| "Last#{i}" }
  • sequence(:company_name) { |i| "Company #{i}" }
  • end
  • ```
  • -This creates ugly sample data like "First3 Last3", but e.g. doesn't make tests pass when they shouldn't.
  • +This creates ugly sample data like "First3 Last3", but e.g. doesn't make tests pass when they shouldn't.
  • +
  • +
  • +### If you must use Faker
  • +
  • +If you must use Faker (e.g. you inherited a large test suite), at least use the same random seed for every test:
  • +
  • +```
  • +# spec/support/faker.rb
  • +
  • +RSpec.configure do |config|
  • + config.prepend_before do
  • + Faker::Config.random = Random.new(42)
  • + Faker::UniqueGenerator.clear
  • + end
  • +end
  • +```
  • +
Profile picture of Henning Koch
Henning Koch
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2026-08-11 08:39)