Rails 07 Feb 2007 05:49 pm
Testing Rails Model Plugins
Extending your Ruby on Rails application with a plugin is very simple: run the code generator to create the template code, then drizzle your yummy application and test code into the supplied directories. Rails automatically adds all plugin lib directories to the $LOAD_PATH. Add a class to your plugin and your Rails application can start using it immediately. You don’t even need a require statement. This is the same mechanism that auto-loads model classes in the app/models directory.
This works nicely for the plugin’s lib directory, but what about its test directory? Unfortunately, by default plugin tests are pretty bland. They use the plain unit test suite supplied by Ruby, and not any of the extended Rails test framework. This will leave our plugin’s test classes with no access to fixtures, database.yml configuration, or any of those nice class auto-loading features.
Fear not! It’s easy to wire the full Rails model testing framework into any plugin. Details below….
(continued)
To get us started, let’s generate stubs for our imaginary SkinnyFeet plugin:
script/generate plugin skinnyfeet create vendor/plugins/skinnyfeet/lib create vendor/plugins/skinnyfeet/tasks create vendor/plugins/skinnyfeet/test create vendor/plugins/skinnyfeet/README create vendor/plugins/skinnyfeet/Rakefile create vendor/plugins/skinnyfeet/init.rb create vendor/plugins/skinnyfeet/install.rb create vendor/plugins/skinnyfeet/uninstall.rb create vendor/plugins/skinnyfeet/lib/skinnyfeet.rb create vendor/plugins/skinnyfeet/tasks/skinnyfeettasks.rake create vendor/plugins/skinnyfeet/test/skinnyfeet_test.rb
The generated test stub is boring as rocks, and completely devoid of Rails-ness:
vendor/plugins/skinnyfeet/test/skinnyfeet_test.rb
require ‘test/unit’
class SkinnyFeetTest < Test::Unit::TestCase
Replace this with your real tests.
def testthisplugin flunk end end
In a test file generated by Rails for a model class, the ${RAILS_ROOT}/test/test_helper.rb file pulls in the Rails environment. In theory, we could reference that application helper file from the plugin, but that’s a bit ugly and dangerous. Our fancy plugin might someday reside in random applications. We have no idea what dastardly hacks they’ve done to their own test_helper.rb file. We certainly don’t want the application test suite to break our plugin test suite.
Let’s be safe and create our own test helper class inside the plugin. The minimum we need is the first three lines of the default generated test_helper.rb file. Note that we have to extend the path to environment.rb by three levels since we’re nested deep in the vendor directory.
vendor/plugins/skinnyfeet/test/testhelper.rb
ENV["RAILSENV"] = “test” require File.expandpath(File.join(File.dirname(FILE), ‘../../../../config/environment’)) require ‘test_help’
Then in each database-hammering plugin test file, replace the require 'test/unit' statement at the top with:
vendor/plugins/skinnyfeet/test/whichevertest.rb
require File.dirname(FILE) + ‘/test_helper’
With this simple change, our plugin gains access to everything we’ve been missing: the application’s database.yml file, the fixtures directory, and the auto-loading feature. We can now start using classes we’ve implemented in our plugin lib directory without bothering with require statements (as long as we follow the proper Rails file-naming conventions).
Also, the fixtures directive should work to load data fixtures into the database for testing — except it’ll grab those fixture files from the default application directory at ${RAILS_ROOT}/test/fixtures/. Instead, we want to create our own fixtures directory inside our plugin, at vendor/plugin/skinny_feet/test/fixtures. To override the default fixture directory (and control other fixture settings), add this to the bottom of the plugin’s test_helper.rb file:
vendor/plugins/skinnyfeet/test/testhelper.rb
class Test::Unit::TestCase self.usetransactionalfixtures = true self.useinstantiatedfixtures = false self.fixturepath = File.expandpath(File.dirname(FILE) + “/fixtures”) end
The disadvantage to loading up Rails for each test suite is that it introduces a significant performance hit. You won’t want to do this for tests that don’t interact with the database.
