[RSpec] Let's master Factory Bot

FactoryBot Basic Key

bin/rails g factory_bot:model user

A file that creates data with FactoryBot is generated as in. This time, the following files will be generated in spec / factories / users.rb.

spec/factories/users.rb


FactoryBot.define do
  factory :user do
  end
end

I will pack the data I want to create in this.

spec/factories/users.rb


FactoryBot.define do
  factory :user do
    name {"Sato"}
    age {20}
    height {170}
  end
end

Let's actually use it.

spec/models/user.rb


require 'rails_helper'

RSpec.describe User, type: :model do
  it "is valid with a name, age and height" do
    expect(FactoryBot.build(:user)).to be_valid
  end
end

You can write it concisely like this. By the way, if you don't use FactoryBot, it will be as follows.

spec/models/user.rb


require 'rails_helper'

RSpec.describe User, type: :model do
  it "is valid with a name, age and height" do
    user = User.new(
      name: "Sato",
      age: 20,
      height: 170,
    )
    expect(user).to be_valid
  end
end

It will look like this, and the number of lines will increase.

That said, it's easier to read, but it hides the data inside FactoryBot, so choose whether to use it or not, depending on your use case.

override

You can override the data in the file where you write the specs.

spec/models/user.rb


require 'rails_helper'

RSpec.describe User, type: :model do
  it "is invalid without a name" do
    user = FactoryBot.build(:user, name: nil)
    user.vaild?
    expect(user.errors[:name]).to include("can't be blank")
  end
end

Overwrite the contents of FactoryBot and change name to nil.

Use sequences to create unique data

For example, "email address" should be a unique value. However, if you use FactoryBot as it is, the same value will be entered every time, so it will get stuck in validation. (Unintentionally.)

A "sequence" is provided to address that issue.

spec/factories/users.rb


FactoryBot.define do
  factory :user do
    name {"Sato"}
    age {20}
    height {170}
    #A sequence is used for email.
    sequence(:email) {|n| "test#{n}@example.com"}
  end
end

This will allow you to always enter a unique value such as [email protected], [email protected] each time a new user is created.

Make the factory more diversified

For example, suppose you want to test an elderly person, or a tall person. The small problem can be absorbed in the factory. There are two main methods.

Use inheritance

spec/factories/users.rb


FactoryBot.define do
  factory :user do
    name {"Sato"}
    age {20}
    height {170}
    sequence(:email) {|n| "test#{n}@example.com"}

    #Data for the elderly
    factory :senior do
      age {75}
    end
  
    #Data for tall people
    factory :tall do
      height {190}
    end
  end
end

You can use it like FactoryBot.build (: user,: senior) or FactoryBot.build (: user,: tall).

Use trait.

I like this one better. The reason is that it can be shortened by writing it in ** FactoryBot **, ** you can see what is "different" **. Let's take a look anyway!

spec/factories/users.rb


FactoryBot.define do
  factory :user do
    name {"Sato"}
    age {20}
    height {170}
    sequence(:email) {|n| "test#{n}@example.com"}

    
    trait :senior do
      age {75}
    end
  
    trait :tall do
      height {190}
    end
  end
end

You can call it by writing something like FactoryBot.build (: user,: senior).

By doing this, the ** features ** of the data are clearly shown, so it is highly readable, isn't it?

Callback

Factory Bot can also represent dynamic movements. Take a look at the code below.

spec/factories/users.rb


FactoryBot.define do
  factory :user do
    name {"Sato"}
    age {20}
    height {170}
    sequence(:email) {|n| "test#{n}@example.com"}

    trait :with_tasks do
      after(:create) { |user| create_list(:task, 5, user: user) }
    end
  end
end

You can create tasks related to user by doing FactoryBot.build (: user,: with_tasks).

Recommended Posts

[RSpec] Let's master Factory Bot
Introduction to RSpec 4. Create test data with Factory Bot
[RSpec] Let's use FactoryBot [Rails]
Introduced Rspec and Factory Bot as a purchasing agency service
Let's unit test with [rails] Rspec!