Skip to content

Instantly share code, notes, and snippets.

@RobAWilkinson
Created April 30, 2015 23:34
Show Gist options
  • Save RobAWilkinson/7315e1d0fd131c70f8b4 to your computer and use it in GitHub Desktop.
Save RobAWilkinson/7315e1d0fd131c70f8b4 to your computer and use it in GitHub Desktop.

Factory Girl

Update Your Gemfile

gem "factory_girl_rails", "~> 4.0"

Once your Gemfile is updated, you'll want to run bundle install.

Configure your test suite

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

Defining factories

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

Using factories

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"

Lazy Attributes

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

Faker

Using Faker with Factory Girl

Faker::Name.name

Associations

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

Inheritance

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment