|
Peter Marklund's Home |
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.
Comments
Anonymous said 4 months ago:
I've also come across this annoyance with inline javascript codeblocks that contains a for loop. The less than (<) in the loop is being treated as an opening tag e.g.:
<script type="text/javascript">
function foo() {
for(var i=0;i<length;i++) {
//do something
}
}
</script>
gives "ignoring attempt to close length with body"




Randy said about 1 year ago:
Hi Mark,
I am coming across this problem but I think I know why. I am using rspec and mocking some models with
mock_model(OrgSpecific::ExcurrActivity, :null_object => true)
From the rspec documentation: "Setting this to true instructs the mock to ignore (quietly consume) any messages it hasn’t been told to expect – and return itself."
When returning itself, it looks something like #<Spec::Mocks::Mock:0x1993260> and it tries to use that to close a tag. It will turn supposedly valid xhtml/xml into invalid xhtml/xml.
Not sure what this post is supposed to accomplish, but it may be an explanation for all of those people that were having issues. I'm still working on a way around it.