Testing Rails Engine Gems

by Justin Ball on June 16th, 2009

I've been working on a number of gems that are basically packaged Ruby on Rails engine plugins. It turns out that turning gems into plugins is pretty easy to do. However, testing them can be a pain. Here are a few things I came up with.

After looking at how Clearance handles tests I've decided that it's ok to embed a basic Rails application in your test directory.

The next trick is getting the gem you are working on to load in your embedded Rails application. The Clearance guys added a file in config/initializers that looks like this:

 
# This simulates loading the clearance gem, but without relying on
# vendor/gems
 
clearance_path = File.join(File.dirname(__FILE__), *%w(.. .. .. ..))
clearance_lib_path = File.join(clearance_path, "lib")
 
$LOAD_PATH.unshift(clearance_lib_path)
load File.join(clearance_path, 'rails', 'init.rb')
 

I thought that was brilliant but it didn't work for me. After messing around in the Rails code for a bit I found a bit of helpful documentation on 'plugin_locators':


The classes that handle finding the desired plugins that you‘d like to load for your application. By default it is the Rails::Plugin::FileSystemLocator which finds plugins to load in vendor/plugins. You can hook into gem location by subclassing Rails::Plugin::Locator and adding it onto the list of plugin_locators.

That sounded like the perfect solution to my problem. I figured I would simply add a plugin locator that pointed to the root of the gem I was building. Turns out that works great. In the embedded rails application inside of config/environment.rb I added this code:

 
class TestGemLocator < Rails::Plugin::Locator
  def plugins
    Rails::Plugin.new(File.join(File.dirname(__FILE__), *%w(.. .. ..)))
  end
end
 
Rails::Initializer.run do |config|
  config.time_zone = 'UTC'
  config.plugin_locators << TestGemLocator
end
 

Now my tests load correctly and all is well. Context is important. If you want to have a look at the full project I put it into a gem called disguise which you can get from github.

  • click down
    I’m a little confused about declaring dependencies within your engine. How can one specify a plugin or gem for the engine itself, rather than having to specify it in the app?


    engine testing
  • jbasdf
    Currently you embed an entire Rails application in the test directory and then refer to the code in the gem with
    Rails::Plugin.new(File.join(File.dirname(__FILE__), *%w(.. .. ..)))

    In Rails 3 you will still embed a test Rails application but you will provide gem bundler with a path to the gem code - File.join(File.dirname(__FILE__), *%w(.. .. ..)) - Then the test app will know where to get it.

    If you need an example of how to do it in Rails 2.3.5 you can pull down one of the gems I've built:
    http://github.com/tatemae/muck-users

    I'll be updating those gems to Rails 3 as soon as it is released.
  • jbasdf
    I talked to Yehuda Katz about this method of testing in Rails 3. Turns out you won't need the TestGemLocator any longer. Instead, you'll only need to require your gem so something like
    require 'clearance' in application.rb will do the trick.

    On a side note Yehuda also mentioned that bundler can load gems from a path so in development it will be possible to point to all of the gem directories you are working on and use those rather than installing each gem and restarting your application.
blog comments powered by Disqus