From Fixtures to Factories «
»


Code: , , , , , ,
Comments Off on From Fixtures to Factories

Automated tests need example data, and it’s a pain to have to construct a complete object in every test, especially when there are a lot of non-optional fields.

The standard improvement is to use fixtures, a file with some example data,
that your tests can load by giving the name of a fixture. Here’s one of the
fixtures for a gamer:


alice: # alice is heavily involved
id: 1
mail: alice@example.com
handle: alice
password: red queen
teaser: Follow the white rabbit.
profile: Not so plain, after all.
homepage: http://www.example.com
jabber: alice@example.org
location_id: 3
last_login: 2006-10-30 11:56:08
created_at: 2006-10-30 11:56:08
location_at: 2006-10-30 11:56:08
tag_text: dnd, wod
mail_on_message: true
mail_on_nearby_discussion: true

This is straightforward, but location_id is the id of another fixture in another file. As you can imagine it’s pretty easy to get them out-of-sync and break some tests. I end up leaving little comments in the YAML to remind myself of the cross-reference.

It’s awfully tempting to reuse fixtures from test to test. Maybe Alice was written to test logging in with a username and password and next I’m writing the test of logging in with an email address and a password. I could copy and paste Alice to another fixture to reuse in that test, but as a programmer I have a pathological aversion to copy and paste. Alice is going to get reused (most of those fields are optional and were used by very different tests), and after this happens a few times it’s hard to change a fixture without breaking an unrelated test.

I’ve gotten a lot of use out of fixtures as I’ve developed the habit of testing all my code, but reuse has made my tests somewhat brittle. After a bit of research, I’ve moved over to using factories.

Here’s the factory for a gamer:


Factory.sequence :handle { |n| "Gamer_#{n}" }

Factory.define :gamer do |g|
g.handle { Factory.next :handle }
g.mail { |m| "#{m.handle}@example.com" }
g.password 'secret'
end

Only the essential, required fields are listed. Using this simple template, the tests themselves specify the values of the fields they’ll be testing.


def test_login_with_valid_username_and_password
gamer = Gamer.create :handle => 'Alice', :password => 'red queen'
...
end
def test_login_with_valid_email_and_password
gamer = Gamer.create :mail => 'alice@example.com', :password => 'red queen'
...
end

Ideally, changing a fixture wouldn’t break any tests at all. And factory_girl (what I’m using for factories in Rails) makes associations easy:


Factory.define :post do |p|
p.association :discussion, :factory => :discussion
p.association :poster, :factory => :gamer
p.created_at Time.now.utc
p.body "Post Body"
end

Now my tests include the specific data they care about, making them easier to understand and improve. And they don’t interrelate, so they’re much more reliable. I do still use fixtures, but now they’re exclusively for tests that need to deal with real-world examples.