Wednesday, March 7, 2012

Adventures in moving from Rails 2.3.14 to 3.0: Controller Spec changes

I use integrate_views (or now render_views) when testing controllers and then, for the most part, don't do separate view tests.

A good portion of my tests check whether a particular element appears on the page. I use have_tag to do this. The rspec-html-matchers gem is a great replacement for the have_tag functionality that was removed from Rspec 2. I wish I had found it before changing many of my have_tag statement to have_selector.

In any case, there are some formatting differences between the new and the old have_tag matcher.

Using css matching
Using the CSS matching with have_tag did not previously require quotes around the value portion of an identifier. For example you could write  have_tag("div[id=my_id]") and it would work. Now, you have to place quotes around the my_id portion, or have_tag("div[id='my_id']"). 

To make this easier, I wrote a regular expression to search for quoteless have_tag matchers and insert the quotes. 

Regex: \[(id|class|href|type)=([^\[^'.]*)\]
Replace: [$1='$2']
Using Textmates find and replace, with the regular expressions checkbox checked, made it relatively pain free to do the replacements. 

Content within a tag
Have_tag used to except a string as a second parameter. The string represents content to search for within the tag. For example have_tag("a[href='people/4']", "John Doe") would search for a link to the 'people/4' path containing the text 'John Doe'.

Though this may still work in some instances, I found that a more consistent way of representing this is to use a :text qualifier: have_tag("a[href='people/4']",  :text => "John Doe"). Use regular expressions I was able to make the following replacement:

Regex: have_tag\((.*),(?: *)(?:"|'|/)(.*)(?:"|'|/)\)
Replace: have_tag($1, :text => /$2/)

This also then used regular expression for the match, instead of straight text.

Matching against response.body, not response
It used to be that you could match the have_tag against the response. But now that causes the following error:
  

     Failure/Error: response.should have_tag("div#group_list") do
     NoMethodError:
       undefined method `current_scope' for #

The have_tag matcher must be applied to the response.body, as in:

response.body.should have_tag("div#group_list")

No comments:

Post a Comment