[rspec-users] response.body.should be_xml_with -- how to do nested xpath matches

Phlip phlip2005 at gmail.com
Sun Mar 8 14:41:43 EDT 2009

David Chelimsky wrote:

>>          xpath :'legend[ contains(., "Personal Information") ]' and
>>          xpath :'label[ contains(., "First name") ]' and
>>          xpath :input, :type => 'text', :name => 'user[first_name]'

> This is nice, but the abstractions are operating at different levels.
> The keyword is "xpath" but the args passed to that look nothing like
> xpath.

That's because it's a DSL. You can use raw XPath, XPath with one convenience 
(:'div[ @class = "content" ]' as a symbol implies "any descendant"), or you can 
use an option hash that generates XPath.

The DSL's benefit is tags like :attribute => foo will automatically escape their 
strings correctly. (That's why the top line of the diagnostic was a little 
cluttered!) foo could contain mixed ' and " payload, and we don't care.

 > How about something like:

> response.body.should be_xml_with do
>   form :action => '/users' do
>     fieldset do
>       legend "Personal Information"
>       label "First name"

Because I'm not webrat?

Yes it would be kewt if label('yo') went into method_missing and came out as 
//label[ contains(., "yo") ]. But I have found that spot checks of algorithmic 
details, such as eRB tags, are more valuable to development than slavishly 
matching your tests to your HTML contents.

If you write this HTML...

  <form blah>
     <input blah>
     <input blah>
     <input blah>

...and if you test it with a sequence of assertions that exactly match it...

   page.should contain
     form blah
       input blah
       input blah
       input blah

...then you are not really doing TDD. You are merely replicating your code as 
your test, and that will only cause irritation at upgrade time or bug time.

Here's an example from our projects at work:

     get :thanks_for_purchasing
     assert_xpath :'center[ . = "Your order has been accepted!" ]'
     assert_xpath :div, :align => :center do
       a = assert_xpath(:'p[ contains(., "questions about your membership") ]/a')
       assert{ a.text == a[:href] }
       assert{ a[:href] == SubscriptionController::CUSTOMER_SERVICE_LINK }

Note how much that specifies in the customer acknowledgment page.

  - the confirmation is centered
  - the explanation div is centered (yes, we could identify it better!)
  - part of the explanation div's contents contains "questions..."
  - next to the "questions..." is an <a>
  - the <a> contains an href matching its own text contents
  - the href links to our customer service system

I call this topic "XPath can see around corners":


We are using XPath to pin down several elements in a page, allowing the other 
elements to change freely, but forcing them to maintain their correlations. That 
test would fail, for example, if the <a> somehow moved away from its 
introductory paragraph.


More information about the rspec-users mailing list