Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating fixture data for model using acts_as_taggable_on

I'm using the acts_as_taggable_on plugin to provide tagging for my Framework model. I've got the functional tests that Rails generates, as well as the fixtures it uses and I would like to expand them to add some tags so that I can test searching by tag, etc.

Do I have to create fixtures for the taggings and tag tables and load them at the top of my functional tests? If so, how do I do that? I haven't gotten my head around the syntax for relations described here. Would an alternative be to grab a Framework instance and add the tags to it before testing the searching behavior? Or will the Rails gods strike me down if I do that?

like image 326
Edward Dale Avatar asked Nov 01 '10 17:11

Edward Dale


4 Answers

I was faced with the same problem. I came up with following approach after I did some trial-and-error, read through documentation about fixtures, associations and @barnaclebarnes answer to this question.

I did this with rails 4.2.0 and acts-as-taggable-on 3.5.0.

Remark: My previous answer did the trick, but was a bit wacky. I completly rewrote it after @David pointed me to a cleaner way - completly ommited my Trial and Error and stuck to the solution.

An approach facilitating more of the on-board facilities of rails

@barnaclebarnes solution would provide a little more automatism, but also means much more typing and bookkeeping on ids. So I kept looking for a more concise way.

Polymorphic relations

acts_as_taggable_on uses a polymorphic relation named taggable to implement the relation between tags and different models. See the rails guide on associations for details on polymorphic relations.

Advanced Fixtures

The rubydoc of ActiveRecord::FixtureSet describes the workings of ActiveRecord and what it does with relations of fixtures (chapter Label references for associations):

Active Record reflects on the fixture's model class, finds all the belongs_to associations, and allows you to specify a target label for the association [...] rather than a target id for the FK [...].

A bit further down on the page, there are also some details on polymorphic belongs_to.

This would allow for a tagging fixture definition like this:

tagging:
  taggable: foo (Klass)
  context: tags
  tag: bar

Namespaced Fixtures and Models

ActiveRecord::TestFixtures provides a method to explicitly set the model class for a fixture in case it can not be inferred. In other words: you can use directories to namespace fixtures and match them with their namespaced models. To load test data for tags and taggings of acts_as_taggable_on we can put their fixtures in the subfolder fixtures/acts_as_taggable_on/ (Remark: fixtures/ActsAsTaggableOn/ would also work)

Put It All Together

# models/item.rb
class Item < ActiveRecord::Base
  acts_as_taggable_on
end

# fixtures/items.yml
item_1: {}
item_2: {}

# fixtures/acts_as_taggable_on/tags.yml
tag_1:
  name: tag_1
tag_2:
  name: tag_2

# fixtures/acts_as_taggable_on/taggings.yml
tagging_1
  taggable: item_1 (Item)
  context: tags
  tag: tag_1

If you erb up the yaml files this allows for a pretty low maintanance definition of taggable models and their instances.

like image 85
Florian Oberleitner Avatar answered Oct 07 '22 00:10

Florian Oberleitner


If you want to use TestUnit then set up some tags (in fixture file tags.yml):

tag_one:
  name: tag one
tag_two:
  name: tag two

And then set up the taggings (in fixture file taggings.yml):

tagging_one:
  tag_id: <%= ActiveRecord::Fixtures.identify(:tag_one) %>
  taggable_id: <%= ActiveRecord::Fixtures.identify(:framework_one) %>
  taggable_type: Framework
  context: tags

Basically the ActiveRecord::Fixtures.identify(:tag_one) gets the ID for the tag to put into the right column.

like image 40
barnaclebarnes Avatar answered Oct 06 '22 23:10

barnaclebarnes


It's generally best to create these kinds of things on the fly as you need them. Fixtures can quickly become pretty unmanageable, and when you look at the test in the future you will need to look at three or four fixture files to unpick what is happening.

I'd recommend taking a little time out to look at the factory_girl gem, it will save you loads of time in the future. You'd do something like this:

# test/factories.rb
Factory.define :framework do |f|
  # Add any properties to make a valid Framework instance here, i.e. if you have
  # validates_presence_of :name on the Framework model ...
  f.name 'Test Name'
end

Then in your functional or unit tests you can easily create objects with the specific properties you need for an individual test:

# Create and save in the DB with default values
@framework = Factory.create(:framework)
# Build an object with a different name and don't save it in the DB
@framework = Factory.build(:framework, :name => 'Other name'
# Create with tags
@framework = Factory.build(:framework, :tags_list => 'foo, bar')
like image 2
malclocke Avatar answered Oct 07 '22 00:10

malclocke


using :tags_list did not work for me:

> undefined method `tags_list=' for #<Project:0xb610b24>

What did work was in your actual factory, you need to add it like such:

Factory.define(:project) do |f|
  f.tags_list ("factory")
end

I have also found that this needs to be in the parent-level factory, for some reason it does not work from children. I have also found that calling

@framework = Factory.build(:framework, :tag_list => 'foo, bar')

Doesn't throw an error, but it quietly does NOT create a tag.

Hope this helps!

like image 1
Dan Zinkevich Avatar answered Oct 06 '22 23:10

Dan Zinkevich