TWIL
Rails
Ruby
Katie showing this week's TWIL blog post highlighting unique value generation with Faker and using FactoryBot in Rails testing.

Welcome back to TWIL, our weekly series that serves as your knowledge espresso on software development insights. This week, Katie illustrates the art of generating unique values with Faker in Ruby and Rails, ensuring the uniqueness of test data with finesse. She also unravels the flexibility of FactoryBot TWILs, guiding us through clever tips for test object creation—talk about an upgrade for your Rails test suite!

Faker: Unique Values

Using Faker for generating test or seed data, it is possible to ensure its outputs are unique when you need them to be.

The following is from the Faker's README:

Prefix your method call with unique. For example:

Faker::Name.unique.name # This will return a unique name every time it is called

If too many unique values are requested from a generator that has a limited number of potential values, a Faker::UniqueGenerator::RetryLimitExceeded exception may be raised. It is possible to clear the record of unique values that have been returned, for example between tests.

Faker::Name.unique.clear # Clears used values for Faker::Name
Faker::UniqueGenerator.clear # Clears used values for all generators

You also can give some already used values to the unique generator if you have collisions with the generated data (i.e: using FactoryBot with random and manually set values).

# Usage:
# Faker::<generator>.unique.exclude(method, arguments, list)
# Add 'azerty' and 'wxcvbn' to the string generator with 6 char length
Faker::Lorem.unique.exclude :string, [number: 6], %w[azerty wxcvbn]

  • Ruby
  • Rails
  • Tests
Katie Linero's profile picture
Katie Linero

Senior Software Engineer


FactoryBot TWILs

When testing in Rails with FactoryBot, there are several ways to build your factories so they are more flexible. (Their documentation is pretty nice, so most of these examples are pulled directly from there.)

Inheritance and Nested Factories

factory :post do
  title { "A title" }

  factory :approved_post do
    approved { true }
  end
end

approved_post = create(:approved_post)
approved_post.title    # => "A title"
approved_post.approved # => true

Dependent Attributes

You can define dependent attributes based on other attributes in your factory; these will reflect values passed in at creation, as well. (This feature is really nice for user generation, like with this email example.)

factory :user do
  first_name { "John" }
  last_name  { "Doe" }
  email { "#{first_name}.#{last_name}@example.com".downcase }
end

create(:user).email
# => "john.doe@example.com"

create(:user, first_name: "Joe").email
# => "joe.doe@example.com"

Transient Attributes

You can use transient attributes in your factory that are not a part of the model your factory is building. This can be useful for conditional formatting/assignment, associations through, etc.

factory :user do
  transient do
    rockstar { false }
  end

  name { "Jane Doe#{" - Rockstar" if rockstar}" }
end

create(:user).name
#=> "Jane Doe"

create(:user, rockstar: true).name
#=> "Jane Doe - ROCKSTAR"

You can also access these in callbacks (which is especially useful in the case of more complex associations, so that your tests don't have to construct all related objects). For example, if users belong to companies through roles:

factory :company do
	name { Faker::Company.name }
end

factory :user do
  name { Faker::Name.unique.name }

  transient do
    company { create(:company) }
  end

  after(:create) do |user, evaluator|
    create(:role, user: user, company: evaluator.company)
  end
    
  factory :admin do
    after(:create) do |user, evaluator|
      create(:role, user: user, company: evaluator.company, admin: true)
    end
  end
end

create(:user).company.name
#=> "Bosco, Durgan and Hickle" # Faker::Company.name

my_company = create(:company, name: "My Company")
create(:user, company: my_company).company.name
#=> "My Company"

You can also do whatever other manipulations you may need in the evaluator block (more details, specifically wrt has_many associations, are explained here).

  • Ruby
  • Rails
  • Tests
Katie Linero's profile picture
Katie Linero

Senior Software Engineer

Related Posts

Image for 'This Week I Learned' blog post featuring tips on managing Git branches including renaming and syncing with remote changes.
October 8, 2019 • Frank Valcarcel

TWIL 2019-10-04

Join us for ‘TWIL,’ where Emily shares innovative Git tips for managing branches with rewritten history. Learn to sync local branches with remote changes and rename them effectively.

Demonstration of GDPR compliant data security practices using handheld device.
June 7, 2018 • Nick Farrell

Techniques for Personal Data Privacy

Over the past decade, we’ve seen what can happen when companies are neglectful with personal data, and in 2018, strong privacy practices can ensure that your company is making headlines for the right reasons.