Wolfmans Howlings

A programmers Blog about Ruby, Rails and a few other issues

Using RSpec to test HAML helpers

Posted by Jim Morris Sat, 14 Jul 2007 23:40:00 GMT

UPDATED for HAML 2.0 and RSpec 1.1.5 - Changed open to haml_tag, prefix helper. to all rspec calls...

The most recent release of HAML introduced a neat feature that allows you to use HAML-like syntax in your helpers to generate HTML HAML#haml_tag.

A question on the HAML news group asked how to test a helper that uses HAML#haml_tag (used to be open/puts) and thanks to Nathan on that list I finally got RSpec to do it. As shown below.

However a really good point was made that really in RSpec the way to test anything is to use mocks to mock any call to an outside method thus focusing the test on the specific module under test. Generally I agree with that philosophy. But this is way cool so I thought I'd do it anyway, and also as it is a new feature in HAML one may not want to simply trust HAML to generate the correct HTML.

So in my application_helper.rb I have a simple helper...

module ApplicationHelper

 ...

  def display_flash
    for name in [:notice, :warning, :error]
      if flash[name]
        haml_tag :div, flash[name], {:class => name.to_s}
      end
    end
    nil
  end

  ...

end

This is called in my views as...

- display_flash

Notice the - instead of =, this is because the open (and puts) write output directly to the HAML buffer, and so this routine should return nothing. (This is also a very simply case and does not show off the utility of the open/puts methods, I'll show one of those later on).

The RSpec helper test that tests this is as follows...

# File: spec/helpers/application_helper_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'

describe ApplicationHelper do

  before :each do
    helper.extend Haml
    helper.extend Haml::Helpers 
    helper.send :init_haml_helpers
  end

  it "should display flash" do
    for name in [:notice, :warning, :error]
      flash[name]= "flash #{name.to_s} message"
      helper.capture_haml{
        helper.display_flash
      }.should =~ /<div class='#{name.to_s}'>\s*#{flash[name]}\s*<\/div>/
      flash[name]= nil
    end
  end

end

Excellent, a simple test for the HTML generated by my haml helper. NOTE the setup required in the before :each, this sets up the haml helpers in the helpers context

Why would I want to use HAML#open you ask?

Well it makes the helpers look so much tidier IMHO, take this example from my previous post on tag clouds, the re-factored helper now looks like this...

  # display a tag cloud for the given model
  def tag_cloud(model, title= nil)
    m= model.to_s.camelcase.constantize
    plural= model.to_s.capitalize.pluralize
    title ||= plural
    tags= m.tag_counts(:order => 'tags.name')
    return false if tags.empty?
    urlmeth= "tagged_#{model.to_s.pluralize}_path".to_sym
    haml_tag :div, {:class => "tagcloud"} do
      haml_tag :h3, title
      tags.each do |t|
        next if t.name == 'FAQ'
        haml_tag :span, {:style => "font-size:#{calc_size(t.count)}%"} do
          puts link_to(h(t.name), self.send(urlmeth, :tag => t.name))
        end
      end
    end
    return true
  end      

So much cleaner, plus I can return a boolean to indicate if there was anything output or not, which tells me if I need to output an <hr/> or not.

Posted in , ,  | Tags , , ,  | 8 comments | no trackbacks

Comments

  1. Evgeny said about 20 hours later:

    I guess the file you will put that in will be spec/helpers/application_spec.rb Because you, and http://rspec.rubyforge.org/documentation/rails/writing/helpers.html don't specify the file naming. I took a guess, and it seems to be okey ...

    Thanks for the helper, and the spec for the helper! :)

    Do you know of any repository of small+usefull helpers that one would usually put in application helpers? That kind of helpers that you take with you to each new application you write ....

    Like for example (that I have in my apps):

    def whitespace(times = 1)
      "&nbsp;" * times
    end
    
  2. wolfmanjm said about 21 hours later:

    Yes that is where I put it.

    I'm not aware of a place for those helpers, but if you find one let me know :)

  3. Evgeny said 4 days later:
      it "should display flash" do
        for name in [ :notice, :warning, :error ]
          flash[name]= "flash #{name.to_s} message"
          capture_haml {
            display_flash
          }.should have_tag "div.#{name.to_s}", 1, :text => flash[name]
        end
      end
    
  4. Evgeny said 4 days later:

    Ohh... I actually just used :

      before(:each) do
        @haml_is_haml = true
        @haml_stack = [Haml::Buffer.new(:attr_wrapper => "'")]
      end
    


    because I have more haml helpers to test than just one.

  5. wolfmanjm said 4 days later:

    Thanks I wasn't sure that have_tag would work, but its good to know thats an option.

  6. Evgeny said 7 days later:

    Is there a way to add those include ActionView::Helpers include Haml::Helpers

    into spec_helper.rb ? and perhaps even the @haml_is_haml and @haml_stack ....

    It's kind of annoying adding those to each description I have of helpers. Not DRY at all. Especially for a non-rails project I am working on.

  7. Evgeny said 7 days later:

    One can also use automatic inclusion for haml helpers for all helper tests
    by editing spec/spec_helper.rb, like so:

    Spec::Runner.configure do |config|
      config.include Haml::Helpers
      config.include ActionView::Helpers
    
      config.before(:each, :behaviour_type => :helper) do
        @haml_is_haml = true
        @haml_stack = [Haml::Buffer.new(:attr_wrapper => "'")]
      end
    end
    

    Or if using Haml trunk, then you can also do it like so:

    Spec::Runner.configure do |config|
      config.before(:each, :behaviour_type => :helper) do
        init_haml_helpers
      end
    end
    

    Though I didn't test this enough, and it could be a bit wrong ... especially regarding the trunk method.

  8. meekish said about 1 month later:

    That's very nice. I learn something new every day.

Trackbacks

Use the following link to trackback from your own site:
http://blog.wolfman.com/articles/trackback/332

(leave url/email »)

   Comment Markup Help Preview comment