Skip to content

Instantly share code, notes, and snippets.

@iandouglas
Created February 11, 2019 04:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iandouglas/1a3fc1268113bab8463114e36db6988a to your computer and use it in GitHub Desktop.
Save iandouglas/1a3fc1268113bab8463114e36db6988a to your computer and use it in GitHub Desktop.
Turing Mod 2 Backend, Rails 5.1 FactoryBot examples

Terminology:

  • ... means "whatever else is already here", don't actually input three dots!

To get started, add the following gem in your Gemfile in the :development, :test group, and run 'bundle update'

group :development, :test do
  ...
  gem 'factory_bot_rails'
end

Next, alter /spec/rails_helper.rb, locate the RSpec.configure block and enter the code as found below.

This tells FactoryBot to reload all objects after each test (helpful if you use before :each blocks. The config.include line makes all FactoryBot commands available without prefixing them all, so we can use create() instead of FactoryBot.create()

Next, create /spec/factories/ as a folder. For this example, we're going to create factories for a User model and an Item model. We'll assume users have different roles and can be made active or inactive, etc.. Items will also have an active/inactive flag, a price, quantity, etc.


Next, create /spec/factories/user.rb for our User model, and enter the code from the file below.

This creates three different factories around the User model. The first factory, :user will generate a known string for the user's name, email and password, set their role to an integer of 0, and set their :active flag to true.

The second factory is called :inactive_user and uses :user as a "parent" template, which copies all attributes from that parent factory. This second factory, then, overrides the name and email address, and also overrides their :active flag to be false.

The last factory here is called :admin, which overrides the name, email and role. This also forces the :active flag to true in case we ever set the :user factory otherwise.


The next factory we're going to make will be for items. Create a new file called /spec/factories/item.rb and enter the code from the file below.

Similar to the :user factory, we can now generate an active :item, or we can use :inactive_item to generate an item with the :active flag overridden to a false state.

Items can also have a numeric price, but this code looks a little strange. We'll discuss that when we read about sequences below.


How to use these factories

user = create(:user)

This generates an instance of the User model and set the appropriate fields. Since we're only creating one of these, their email address will be user_1@gmail.com, their name will be User Name 1 etc.

Calling this a second time will increment the 'sequence' number (more on sequences below), so calling the create() method a second time will generate a user whose email address will be user_2@gmail.com etc..

When we create an item, we see that the sequence does some math for us with the price and quantity. Our factory says "whatever the sequence is, add 1 to it, then multiply that number by 1.5 to set its price. Therefore, the first item we create() will have a price of $3.00, the second item will have a price of $4.50, and so on.

The nice thing about FactoryBot is that we can also generate lots of these objects in one shot.

user_1, user_2, user_3 = create_list(:user, 3)

Here, we use create_list() to make an array of these objects. We can also assign them to an array:

a_whole_lot_of_users = create_list(:user, 100)

What are sequences?

FactoryBot will allow us to make one or more items and increment a counter. This comes in really handy for giving every user a unique name or email address, or use different images, etc..

However, as you noticed in the :item factory, FactoryBot's sequence generator doesn't handle numbers in a clean way so we have to do some Ruby code to string interpolate the sequence number, then turn that into a numeric data type.

FactoryBot.define do
factory :item, model: Item do
sequence(:name) { |n| "Item Name #{n}" }
sequence(:description) { |n| "Description #{n}" }
sequence(:image) { |n| "https://picsum.photos/200/300?image=#{n}" }
sequence(:price) { |n| ("#{n}".to_i+1)*1.5 }
active { true }
end
factory :inactive_item, parent: :item do
sequence(:name) { |n| "Inactive Item Name #{n}" }
active { false }
end
end
FactoryBot.define do
factory :user, class: User do
sequence(:email) { |n| "user_#{n}@gmail.com" }
sequence(:password) { "password" }
sequence(:name) { |n| "User Name #{n}" }
role { 0 }
active { true }
end
factory :inactive_user, parent: :user do
sequence(:name) { |n| "Inactive User Name #{n}" }
sequence(:email) { |n| "inactive_user_#{n}@gmail.com" }
active { false }
end
factory :admin, parent: :user do
sequence(:email) { |n| "admin_#{n}@gmail.com" }
sequence(:name) { |n| "Admin Name #{n}" }
role { 1 }
active { true }
end
end
# leave everything above this area of code
# locate this RSpec.configure block:
RSpec.configure do |config|
# leave whatever else you find in this area of the file
# add the next four lines of code
config.after(:each) do
FactoryBot.reload
end
config.include FactoryBot::Syntax::Methods
end
# leave everything that comes after this area of code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment