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 `{lang=”yaml”}

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 `{lang=”ruby”}

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 `{lang=”ruby”}

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.