Using RSpec to test HAML helpers
Posted by Jim Morris on Sat Jul 14 16:44:33 -0700 2007
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.