April 24, 2009

Webrat with RSpec, no Cucumber

For the impatient: install webrat as from installation instructions and then add this section to your spec_helper.rb that lets you create webrat specs in spec/integration folder:

module Spec::Rails::Example
  class IntegrationExampleGroup < ActionController::IntegrationTest

   def initialize(defined_description, options={}, &implementation)
     defined_description.instance_eval do
       def to_s
         self
       end
     end
     
     super(defined_description)
   end

    Spec::Example::ExampleGroupFactory.register(:integration, self)
  end
end

There is plenty of examples of usage of Webrat alone inside test/integration but very few using RSpec. “No big deal” I thought, it must be just as simple as from test/integration. Why I should skip Cucumber? I hope to use Cucumber in the next projects, but I don’t have a real customer for the internal app I’m writing right now and it doesn’t make a lot sense to be so english-like for requirements. So after installing RSpec[Rails] 1.2.4 on Rails 2.3.2 and scaffolded some examples with “./script/generate rspec_scaffold post title:string body:text published:boolean” I created the following webrat-enabled view spec in spec/views/verify_posts_webrat_spec.rb:

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe "Creating blog posts" do
  
  it "should display the list of posts" do
    visit posts_url
    assert_contain "Listing posts"
  end

end

and with the following spec_helper setup:

    1 ENV["RAILS_ENV"] ||= 'test'
    2 require File.dirname(__FILE__) + 
    3 "/../config/environment" unless defined?(RAILS_ROOT)
    4 require 'spec/autorun'
    5 require 'spec/rails'
    6 
    7 Webrat.configure do |config|
    8     config.mode = :rails
    9 end
   10 
   11 Spec::Runner.configure do |config|
   12   config.use_transactional_fixtures = true
   13   config.use_instantiated_fixtures  = false
   14   config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
   15   include Webrat::Methods
   16 end
   17 

which gives a sad “undefined method `https!’ for #Spec::Rails::Example::ViewExampleGroup::Subclass_1:0x237e5f4”. Notice line 7-9 and 15 of the spec_helper, those are the correct settings I think. I couldn’t find any reason why this shouldn’t work and I try to move the spec into the controllers or models folders just to be sure. But nothing. I gave up but the following day I thought that if I had to make a choice about the position of webrat based specs that would be in the spec/integration folder. So I decided to create a new RSpec example group so that whatever happens to be in the integration folder is associated with a specfic class that I can control. So I added the following to spec_helper:

    1 ENV["RAILS_ENV"] ||= 'test'
    2 require File.dirname(__FILE__) + 
    3   "/../config/environment" unless defined?(RAILS_ROOT)
    4 require 'spec/autorun'
    5 require 'spec/rails'
    6 
    7 Webrat.configure do |config|
    8     config.mode = :rails
    9 end
   10 
   11 Spec::Runner.configure do |config|
   12   config.use_transactional_fixtures = true
   13   config.use_instantiated_fixtures  = false
   14   config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
   15 end
   16 
   17 module Spec::Rails::Example
   18   class IntegrationExampleGroup < ActionController::IntegrationTest
   19 
   20    def initialize(defined_description, options={}, &implementation)
   21      defined_description.instance_eval do
   22        def to_s
   23          self
   24        end
   25      end
   26      
   27      super(defined_description)
   28    end
   29 
   30     Spec::Example::ExampleGroupFactory.register(:integration, self)
   31   end
   32 end

and moved verify_posts_webrat_spec.rb to spec/integration et voila’, green bars. A few things to notice:

  • Line 17: inheriting from ActionController::IntegrationTest brings in all the Rails testing integration goodness
  • Line 20-28: this constructor definition is the trick necessary to intercept the instance of ExampleProxy and inject a “to_s” that returns the object itself. The trick is necessary because in ActionController::IntegrationTest the initializer is calling to_s on the incoming parameter expecting always a symbol (but we are passing an ExampleProxy instead)

If you try this solution, I’m pretty sure it won’t work for your combination of Rails/RSpec/Webrat/Environment version. Add a comment below so I can fix and update this post. Happy coding.

20090603 UPDATE: fixed for rspec 1.2.6.
Comments (View)
blog comments powered by Disqus