Plasticx Blog

Capable of being shaped or formed

Shoulda macros for rendered partials and globbed routes

Posted by Mike 11/19/2009 at 10:19AM

Here are a couple of Shoulda macros that I’ve been using. One is to validate that partials are rendered in a view and the other is to validate globbed routes.

should_render_partial

As the name the name implies this macro validates that a partial has been rendered. The macro was born out of the need to test partials being rendered in an implementation of the Presenter Pattern that I wrote. The presenter I wrote was for Appstatz.com a site my friend Shane Vitarana created for tracking iPhone application sales and downloads. The graphs on the site are displayed with the Bluff JavaScript graphing library. The data used by Bluff in each kind of graph was rendered with a composition of different partials. The Presenter Pattern that was implemented was driving which partials were to be rendered based on the state of the application and thus tests were written to validate the implementation of the pattern is acting as expected.

Using should_render_partial takes absolute or relative paths as strings or symbols as its argument and is as easy as this example

class FoosControllerTest < ActionController::TestCase
  context "a beautiful Bluff graph" do
    setup do
      @foo = Factory :foo
      get :show, :id => @foo.id
    end

    should_render_partial 'layouts/_logo'
    should_render_partial :_data
    should_render_partial :_summary
  end
end

The code for should_render_partial is listed below and a Gist of the code is listed at http://gist.github.com/237938

# shoulda validation that a partial has been rendered by a view
 
class Test::Unit::TestCase
 
   def self.should_render_partial(partial)
     should "render partial #{partial.inspect}" do
       assert_template :partial => partial.to_s
     end
   end
 
end

Shoulda loads macros from the test/shoulda_macros/ directory therefore add the macro code to a file in that directory.

should_route_glob

Again, as the name implies should_route_glob tests that if there is a globbing route specified in the config/routes.rb then it is acting as expected. Globbed routes should be the last route mapping in the config/routes.rb file as it will greedily respond to all requests. This kind of routing is used in content management systems and I’ve also seen it used in specialized 404 handlers. For instance if an application is ported to Rails, adding a final controller route that accepts all requests would be useful to track down legacy requests. These requests, their URI and parameters, would be stored in a table so they can be inspected later. Using this technique one can easily find legacy routes that are not be handled by the new controllers, or unexpected routes that are exposed from buggy Ajax requests or odd user input, etc.

The routing (implying a controller named Foo) and its functional test are listed below.

ActionController::Routing::Routes.draw do |map|
  # GET /a/b/c will be exposed to the action as an array in params[:path] and it
  # will have already been delimited by the '/' character in the requested path
  map.any '*path', :controller => 'foos', :action => 'index'
end

The test code is as follows.

class FoosControllerTest < ActionController::TestCase
  should_route_glob :get, '/a/b/c', :action => 'show', :path => 'a/b/c'.split('/')
end

The code for should_route_glob is listed below and a Gist of the code is listed at http://gist.github.com/237987 This code may be a bit verbose as it appears that (as of 11/18/2009) Shoulda is handling globbed routes better. Add a comment if you improve this should_route_glob macro. Shoulda loads macros from the test/shoulda_macros/ directory therefore add the code to a file in that directory.

class Test::Unit::TestCase

  def self.should_route_glob(method, path, options)
    unless options[:controller]
      options[:controller] = self.name.gsub(/ControllerTest$/, '').tableize
    end
    options[:controller] = options[:controller].to_s
    options[:action] = options[:action].to_s

    populated_path = path.dup
    options.each do |key, value|
      options[key] = value if value.respond_to? :to_param
      populated_path.gsub!(key.inspect, value.to_s)
    end

    should_name = "route #{method.to_s.upcase} #{populated_path} to/from #{options.inspect}"

    should should_name do
      assert_routing({:method => method, :path => populated_path}, options)
    end
  end

end

Posted in , , |


Web Statistics