Fixin’ Fixtures

Tháng Hai 17, 2009

Source: http://errtheblog.com/posts/61-fixin-fixtures

The main problem with fixtures, for me, has always been how unfun they are. They literally suck the fun out of anything they’re around. You throw them in your test/ directory, then suddenly testing is, like, work. But it’s not work, dammit. This is Ruby, dammit.

So, keep them fun. Write your fixtures in Ruby.

Fixtures in Ruby

Step 1: FixtureScenarios

Tom Preston-Werner has this FixtureScenarios plugin. The project home page explains:

This plugin allows you to create ‘scenarios’ which are collections of fixtures and ruby files that represent a context against which you can run tests.

Pretty cool. Instead of just test/fixtures, you can have (for instance), test/fixtures/users and test/fixtures/beta. Then, in your test class, you call scenario :users in place of the normal fixtures :users, :posts, :images call. This will pull all the YAML files from test/fixtures/users into your test database. When you need a completely separate set of fixtures for another feature, it’s as easy as scenario :beta.

Scenarios can also be nested. You can create a test/fixtures/users/banned directory filled with YAMLy files and call it with scenario :banned. This’ll pull in both the test/fixtures/users fixtures and all the test/fixtures/users/banned fixtures. It will also, by default, pull in all the test/fixtures fixtures.

The real beauty of this is when you load up your code six months down the road, recently returned from your jaunt up Everest, and need to add a feature. Fast! Just add a new scenario—forget about worrying on your old tests, they won’t be affected.

This plugin is also great when BDDin’ with test/spec or RSpec. You can have separate contexts in one spec file, all using different fixture scenarios as needed. Maybe you want to assert hardcore pagination behavior—you can load up 10,000 records! But only for that one scenario, leaving your other scenarios sane.

Grabbit:

$ script/plugin install http://fixture-scenarios.googlecode.com/svn/trunk/fixture_scenarios

Step 2: FixtureScenarioBuilder

Okay, like I said, we need to write our fixtures in Ruby.

By default, FixtureScenarios will load any Ruby files it finds in your fixture directories. Cool, but not really sufficient. We need transactional fixtures, we need the database cleared out after test runs, we need a clean database when tests start. We really need all the cool features YAML fixtures offer.

And we can get them, with FixtureScenarioBuilder. This is an add-on to Tom’s FixtureScenarios plugin. AKA you need FixtureScenarios for it to work.

Grabbit:

$ script/plugin install \ svn://errtheblog.com/svn/plugins/fixture_scenarios_builder

Step 3: scenarios.rb

After you’ve installed the plugin, create a scenarios.rb file and place it in your test/fixtures directory. It is in this file you build your fixture scenarios. In Ruby.

Like this:

scenario :blog do
  %w( Tom Chris Kevin ).each_with_index do |user, index|
    User.create! :name => user, :banned => index.odd?
  end
end

As soon as I run a test, test/fixtures/blog will get created with a users.yml file inside it. It’ll look like this:

chris:
  name: Chris
  id: "2"
  banned: "1"
  updated_at: 2007-05-09 09:08:04
  created_at: 2007-05-09 09:08:04
kevin:
  name: Kevin
  id: "3"
  banned: "0"
  updated_at: 2007-05-09 09:08:04
  created_at: 2007-05-09 09:08:04
tom:
  name: Tom
  id: "1"
  banned: "0"
  updated_at: 2007-05-09 09:08:04
  created_at: 2007-05-09 09:08:04

Notice we’ve got intelligent fixture names. FixtureScenarioBuilder did its best to guess these by trying name, username, then title. If it can’t figure out a name, it falls back to stories_001 or whatever.

As for quick, iterative, agile development: whenever you modify scenarios.rb, your YAML fixtures will get re-built. Any errors in your scenarios.rb file will be clearly surfaced when building.

FixtureScenarioBuilder also supports nesting fixtures:

scenario :blog do
  %w( Tom Chris ).each do |user|
    User.create! :name => user
  end
end

scenario :banned_users => :blog do
  User.create! :name => 'Kevin', :banned => true
end

As you may guess, this creates test/fixtures/blog and test/fixtures/blog/banned_users. Rake-like and tasty.

Ruby fixtures are super because you can easily throw around associations, forget about those updated_at & created_at fields, easily refactor fixtures after making sweeping schema changes, and don’t need to worry about join tables.

I’ve been doing fixtures this way for a few months now and really dig. FixtureScenarios and FixtureScenarioBuilder are like a one-two knock out punch of… uh… test data.

Step 4: Real Life And All

For a more complex example, check out this scenarios.rb file. It uses nesting, multiple scenarios, and all sorts of Ruby goodness (for a code review site, ya heard).

You can get a lot of this stuff by ERBing your YAML files, but really, that sucks. I like having my habtm join tables generated for me, I am comfortable defining associations and whatnot in Ruby. It’s how I think.

These days, many people are hacking their database or mocking to get around the fixture pain. Other fixture solutions are the fixture references plugin, this patch, and QuickFix. I’m sure there are countless others. Got a good solution? Leave a comment, plz.

Step 5: Get Out of Here

For further explanation, check out the README. Browse the code. Find a bug. Etc.

Thanks, and goodnight.

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

%d bloggers like this: