Monday, December 15, 2008

Using rspec-rails during gem development

Update:

I been having all sorts of problems using this approach after updates in rspec and rspec-rails and no longer recommend using it!

The source code has been updated to use another approach in which I include a minimal Rails application in the gem, to run tests against. Until I get a chance to write a more complete post on this new approach take a look at the source, available at GitHub.

Not long ago I started using rspec and rspec-rails for testing in my Rails apps. I've quickly became hooked on Behaviour Driven Design (BDD) and feel it has boosted the quality (and readability) of my tests.

But when I wanted to move a view helper from one project into a gem for easy reuse in other apps it took me a while to figure out how to get the tests to run in isolation.

The main issue I had was the rspec-rails gem relying on there being a rails project present which of course isn't the case with a stand-alone gem. Specifically, it wants to load the project's ApplicationController which in turn will load a whole bunch of other stuff.

The second problem I ran into was how to write tests for helpers that output text with a non-output code block (i.e., <% ... %>).

Let's look at the stripped down version of the rspec for my boxed_content helper that wraps its containing block in a number of divs:
[sourcecode lang="ruby" gist="35970"]require File.expand_path(File.dirname(__FILE__) ) + "/../spec_helper"


describe "boxed_content", :type => :helper do

it "should set default CSS classes" do
boxed_content.should have_tag("div") do
with_tag("div.box_bottom_left") do
with_tag("div.box_bottom_right") do
with_tag("div.box_top_left") do
with_tag("div.box_top_right")
end
end
end
end
end

def boxed_content(params = {})
eval_erb("<% boxed_content(#{params.inspect}) do %> <%end%>")
end

end [/sourcecode]
When testing gems I don't rely on the specs to follow the directory layout defined by rspec-rails. Instead I pass the :helper type on line 3 so the created example group is a HelperExampleGroup.

As the boxed_content helper will not output text on its own I use eval_erb to execute it. This done in the method on lines 17-19. Putting it in a method makes the test code easier to read.

Ok. On to the spec_helper.rb where rspec and rspec-rails are set up:
[sourcecode lang="ruby" gist="35998"]require 'rubygems'


require 'action_controller'

class ApplicationController < ActionController::Base
end

require 'action_controller/test_process'
require 'action_controller/integration'
require 'active_record/fixtures' if defined?(ActiveRecord::Base)
require 'test/unit'

require 'spec'

require 'spec/rails/matchers'
require 'spec/rails/mocks'
require 'spec/rails/example'
require 'spec/rails/extensions'
require 'spec/rails/interop/testcase'

require File.expand_path(File.dirname(__FILE__) + "/../lib/robins_html_helpers")
[/sourcecode]
The trick here is on line 5 where I create an ApplicationController class. This will load a bunch of stuff and allow us to continue with the rest of the require statements for rspec and rspec-rails. Lines 8-19 are copied from rspec-rails initialization.

Finally on line 21, my module containing the helpers i want to test, is loaded.

Resources


1 comment:

  1. @Todd Tyree
    Thanks for pointing this out.
    From your stack trace I see that my rspec and rspec-rails gem versions differ from yours. I was on 1.1.11 and both 'rake spec' and 'spec ./spec' could be used to run the specs.
    When upgrading my rspec and rspec-rails gems to 1.1.12 I get the same stack trace as you do.

    I will look into this and post back when done.

    /Robin

    ReplyDelete