[rspec-users] how to write nested XPath matchers

Phlip phlip2005 at gmail.com
Mon Feb 16 10:36:32 EST 2009

> This looks pretty cool. I wonder if you'd have any interest in making
> this a bit more rspec-friendly? Something like an option to run it
> like this:

Here's the spec. The sauce is below my signature.

   it 'should have a user form with the first name' do
     render '/users/new'
     response.body.should be_xml_with do
       xpath :form, :action => '/users' do
         xpath :fieldset do
           xpath :'legend[ contains(., "Personal Information") ]' and
           xpath :'label[ contains(., "First name") ]' and
           xpath :input, :type => 'text', :name => 'user[first_name]'

Now, two issues. Firstly, what is the point of writing verbiage, designed for 
review by the customer team, if they should not be expected to understand 
hardcore engineering gibberish beginning with "body.should be_xml_with do"? A 
sentence that is only partly English does more harm than good!

Secondly, when an xpath() fails, it prepares an elaborate and detailed analysis 
of the entire situation, packs this into a flunk(), and raises it in a 

Then RSpec then throws all that stuff away, and provides an incorrect stack 
trace to an internal error. Switching my 'user[first_name]' to 
'user[first_nome]' provides this:

NoMethodError in '/users/new should have xpathic tags'
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.first

So if I were to rescue my own AssertionFailedError, and pack it into a 
failure_message, wouldn't that effort be redundant?


require 'assert2/xpath'

Spec::Runner.configure do |c|
   c.include Test::Unit::Assertions
end  #  TODO blog this

   class BeXmlWith

     def initialize(scope, &block)
       @scope, @block = scope, block

     def matches?(stwing, &block)
       waz_xdoc = @xdoc
       @block = block if block
       @scope.assert_xhtml stwing
       return (block || @block || proc{}).call
       @xdoc = waz_xdoc

     def failure_message
       "yack yack yack"

     def negative_failure_message
       "yack yack yack"

   def be_xml_with(&block)
     BeXmlWith.new(self, &block)

More information about the rspec-users mailing list