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