Creating Fixtures for Many-to-Many Associations
Problem
Creating test fixtures for simple tables that don't have any relations to other tables is easy. But you have some Active Record objects with many-to-many associations. How do you populate your test database with data to test these more complex relationships?
Solution
Your database contains assets and tags tables as well as a join table named assets_tags. The following migration sets up these tables:
db/migrate/001_build_db.rb:
class BuildDb < ActiveRecord::Migration
def self.up
create_table :assets do |t|
t.column :name, :string
t.column :description, :text
end
create_table :tags do |t|
t.column :name, :string
end
create_table :assets_tags do |t|
t.column :asset_id, :integer
t.column :tag_id, :integer
end
end
def self.down
drop_table :assets_tags
drop_table :assets
drop_table :tags
end end
The Asset and Tag classes have Active Record many-to-many associations with each other:
app/models/asset.rb:
class Asset < ActiveRecord::Base
has_and_belongs_to_many :tags end
app/models/tag.rb:
class Tag < ActiveRecord::Base
has_and_belongs_to_many :assets
end
To create YAML test fixtures to populate these tables, start by adding two fixtures to tags.yml:
test/fixtures/tags.yml:
travel_tag:
id: 1
name: Travel office_tag:
id: 2
name: Office
Likewise, create three asset fixtures:
test/fixtures/assets.yml:
laptop_asset:
id: 1
name: Laptop Computer desktop_asset:
id: 2
name: Desktop Computer projector_asset:
id: 3
name: Projector
Finally, to associate the tags and assets fixtures, we need to populate the join table. Create fixtures for each asset in assets_tags.yml with the id from each table:
test/fixtures/assets_tags.yml:
laptop_for_travel:
asset_id: 1
tag_id: 1
desktop_for_office:
asset_id: 2
tag_id: 2
projector_for_office:
asset_id: 3
tag_id: 2
Discussion
You include one or more fixtures by passing them as a comma-separated list to the fixtures method. By including all three fixture files in your test case class, you'll have access to assets and can access their tags:
test/unit/asset_test.rb:
require File.dirname(__FILE__) + '/../test_helper'
class AssetTest < Test::Unit::TestCase
fixtures :assets, :tags, :assets_tags
def test_assets
laptop_tag = assets('laptop_asset').tags[0]
assert_kind_of Tag, laptop_tag
assert_equal tags('travel_tag'), laptop_tag
end end
See Also
|