Peter Marklund

Peter Marklund's Home

Sat Mar 24 2007 16:35:50 GMT+0000 (Coordinated Universal Time)

Rails Testing: Making assert_select XML safe

I've been using the new assert_select command extensively in my tests lately and it's a wonderfully powerful tool - a huge improvement over assert_tag. The only annoyance has been the warning messages "ignoring attempt to close form with link" that have been polluting my test output. The other day I decided to track the source of the messages and I discovered that others had already complained about them. I found that the root cause of the problem lies in the HTML::Document and the HTML::Tag classes that ship with Rails. More specifically the problem is in the HTML::Tag#childless? method that auto-closes HTML tags such as img, br, and hr.

What do I mean by auto-closing. Well, in XML you have to close all tags, either with a trailing slash, or with a closing tag. In HTML however, we use tags such as <br> and <link>, usually without a trailing slash. The HTML::Tag class takes those special tags into account and understands that they are auto-closing (childless) without the trailing slash. This becomes a problem if you have an XML document that contains <link>some content</link> since the parser will think the first tag is self-closing and the second tag will be a mismatch. Not only does this cause warning or error messages, but it breaks assert_select for those specific tags.

I have submitted a patch that makes HTML::Document enter XML mode if the document has an <?xml declaration. Jamis has posted about his approach which he calls assert_xml_select. However, since all my documents are XML (XHTML or VoiceXML in my current project) I chose the following approach instead which allows me to use assert_select:

if RAILS_ENV == 'test'
  module ActionController #:nodoc:
    module TestProcess
      # Work around Rails ticket http://dev.rubyonrails.org/ticket/1937
      def html_document
        @html_document ||= HTML::Document.new(@response.body, true, true)
      end
    end
  end
end

Setting the first boolean argument to true makes the parser error out if there is an unclosed or mis-matched tag. I want my documents to be valid and I like to fail early.