Last Updated: February 23, 2017
·
577
· jfragoulis

Replace your static fixtures with factories using factory_girl

Most of the time we use factory_girl to create ActiveRecord objects when in fact we can use it to create any type of object.

We use factory_girl to create hash objects for request bodies and responses in order to test our rest api, as well as responses from external services.It is a simple integration to make, does not affect the way you handle json responses and provides several benefits.

Let's say there is a rest api with an endpoint to get user info:

GET /api/user/1
{
  "id": 1,
  "username": "foo",
  "email": "foo@bar.com"
}

and you are writing a client for that api which contains a method for calling that endpoint. To test this method you want to mock the request to return a fixture, so you probably create one as such (under spec/fixtures/user.json):

{
  "id": 1,
  "username": "foo",
  "email": "foo@bar.com"
}

In the specs you would write something like:

describe '#get_user' do
  subject { client.get_user(user_id) }
  let(:user_id) { 1 }
  let(:expected_user) { load_fixture(:user) }
  before { mock(get_user_by_id_url).to return expected_user }

  it 'returns the user' do
    is_expected.to eq expected_user
  end
end

Although this is an oversimplified example, it should be sufficient to make my point.

This fixture is static, so when for some reason you want add some context, test a conditional, a special case or anything else and you need another user fixture (i.e a user with at least one different attribute) you would most likely create a second fixture (i.e user_without_email.json).

You can utilize factory_girl to replace the static fixture with something that has much more future potential and reusability, without affecting anything else.

Create the factory:

# spec/factories/user.rb
FactoryGirl.define do
  factory :user, class: Hash do
    id 1
    username 'foo'
    email { "#{username}@bar.com" }
  end
end

The thing to notice here is class: Hash, which means the factory will try to create a hash object with those attributes. Then in your spec you can replace:

let(:expected_user) { load_fixture(:user) }

with

let(:expected_user) { FactoryGirl.attributes_for(:user) }

which will give you exactly the same result, except now you can also on the fly override attributes

let(:expected_user) { FactoryGirl.attributes_for(:user, email: nil) }

or add traits

# spec/factories/user.rb
FactoryGirl.define do
  factory :user, class: Hash do
    id 1
    username 'foo'
    email { "#{username}@bar.com" }

    trait :without_email do
      email nil
    end
  end
end

or add subfactories and aliases. There is also the list equivalent attributes_for_list and you can also use random data generators (like faker and such).