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
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