gem "factory_girl_rails", "~> 4.0"
Once your Gemfile is updated, you'll want to run bundle install
.
# RSpec
# spec/support/factory_girl.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
If you do not include FactoryGirl::Syntax::Methods
in your test suite, then all factory_girl methods will need to be prefaced with FactoryGirl
.
Each factory has a name and a set of attributes. The name is used to guess the class of the object by default, but it's possible to explicitly specify it:
# This will guess the User class
FactoryGirl.define do
factory :user do
first_name "John"
last_name "Doe"
admin false
end
# This will use the User class (Admin would have been guessed)
factory :admin, class: User do
first_name "Admin"
last_name "User"
admin true
end
end
It is highly recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class.
Attempting to define multiple factories with the same name will raise an error.
Factories can be defined anywhere, but will be automatically loaded if they are defined in files at the following locations:
spec/factories.rb
spec/factories/*.rb
factory_girl supports several different build strategies: build, create, attributes_for and build_stubbed:
# Returns a User instance that's not saved
user = build(:user)
# Returns a saved User instance
user = create(:user)
# Returns a hash of attributes that can be used to build a User instance
attrs = attributes_for(:user)
# Returns an object with all defined attributes stubbed out
stub = build_stubbed(:user)
# Passing a block to any of the methods above will yield the return object
create(:user) do |user|
user.posts.create(attributes_for(:post))
end
No matter which strategy is used, it's possible to override the defined attributes by passing a hash:
# Build a User instance and override the first_name property
user = build(:user, first_name: "Joe")
user.first_name
# => "Joe"
Most factory attributes can be added using static values that are evaluated when the factory is defined, but some attributes (such as associations and other attributes that must be dynamically generated) will need values assigned each time an instance is generated. These "lazy" attributes can be added by passing a block instead of a parameter:
factory :user do
# ...
activation_code { User.generate_activation_code }
date_of_birth { 21.years.ago }
end
Using Faker with Factory Girl
Faker::Name.name
It's possible to set up associations within factories. If the factory name is the same as the association name, the factory name can be left out.
factory :post do
# ...
author
end
You can also specify a different factory or override attributes:
factory :post do
# ...
association :author, factory: :user, last_name: "Writely"
end
The behavior of the association method varies depending on the build strategy used for the parent object.
# Builds and saves a User and a Post
post = create(:post)
post.new_record? # => false
post.author.new_record? # => false
# Builds and saves a User, and then builds but does not save a Post
post = build(:post)
post.new_record? # => true
post.author.new_record? # => false
To not save the associated object, specify strategy: :build in the factory:
factory :post do
# ...
association :author, factory: :user, strategy: :build
end
# Builds a User, and then builds a Post, but does not save either
post = build(:post)
post.new_record? # => true
post.author.new_record? # => true
Please note that the strategy: :build
option must be passed to an explicit call to association
,
and cannot be used with implicit associations:
factory :post do
# ...
author strategy: :build # <<< this does *not* work; causes author_id to be nil
Generating data for a has_many
relationship is a bit more involved,
depending on the amount of flexibility desired, but here's a surefire example
of generating associated data.
FactoryGirl.define do
# post factory with a `belongs_to` association for the user
factory :post do
title "Through the Looking Glass"
user
end
# user factory without associated posts
factory :user do
name "John Doe"
# user_with_posts will create post data after the user has been created
factory :user_with_posts do
# posts_count is declared as a transient attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
posts_count 5
end
# the after(:create) yields two values; the user instance itself and the
# evaluator, which stores all values from the factory, including transient
# attributes; `create_list`'s second argument is the number of records
# to create and we make sure the user is associated properly to the post
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
end
end
This allows us to do:
create(:user).posts.length # 0
create(:user_with_posts).posts.length # 5
create(:user_with_posts, posts_count: 15).posts.length # 15
You can easily create multiple factories for the same class without repeating common attributes by nesting 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
You can also assign the parent explicitly:
factory :post do
title "A title"
end
factory :approved_post, parent: :post do
approved true
end
As mentioned above, it's good practice to define a basic factory for each class with only the attributes required to create it. Then, create more specific factories that inherit from this basic parent. Factory definitions are still code, so keep them DRY.