<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Talk Like A Duck: Validating RSS Feeds with RSpec</title>
    <link>http://talklikeaduck.denhaven2.com/articles/2008/02/08/validating-rss-feeds-with-rspec</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>In Ruby, it's not the dog, it's the tricks!</description>
    <item>
      <title>Validating RSS Feeds with RSpec</title>
      <description>&lt;p&gt;One of my current Rails projects involves generating an RSS feed.  While I was working on this the other night, it seemed to be working, so I deployed it to the staging server. Everything looked fine. If I fetched it with Firefox, the browser offered to let me subscribe to the feed with Google Reader, and if I used Safari I'd see a nice view of the feed just like I expected.&lt;/p&gt;
&lt;p&gt;So I sent a note via our campfire to check it out, and a colleague replied that &lt;strong&gt;his&lt;/strong&gt; Safari was saying that it was in an invalid format.&lt;/p&gt;
&lt;p&gt;So I went to the &lt;a href="http://validator.w3.org/feed/"&gt;W3 RSS Feed Validation Service&lt;/a&gt; and worked through the validation issues, after which his browser was as happy as mine.&lt;/p&gt;
&lt;p&gt;Of course, having been through that, I wanted to make sure that RSS validation was covered in the specs for the project.&lt;/p&gt;
&lt;p&gt;I went looking for an existing RSpec matcher, and found &lt;a href="http://www.anodyne.ca/2007/09/28/rspec-custom-matchers-and-be_valid_xhtml/"&gt;a matcher to validate XHTML&lt;/a&gt; but nothing for RSS.&lt;/p&gt;
&lt;p&gt;I then found the &lt;a href="http://feedvalidator.rubyforge.org/"&gt;feedvalidator gem&lt;/a&gt; which provides a Ruby interface to the SOAP interface to the W3 feed validator.  You would think that W3 would be providing a REST interface!  The gem already provides assertions for use with Test::Unit, so I just built an RSpec matcher.&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;BeValidFeed&lt;/span&gt;
  &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;feed_validator&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;tmpdir&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;md5&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;matches?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;validity_checks_disabled?&lt;/span&gt;
    &lt;span class="ident"&gt;v&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;W3C&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;FeedValidator&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
    &lt;span class="ident"&gt;fragment&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;body&lt;/span&gt;
    &lt;span class="ident"&gt;filename&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt; &lt;span class="constant"&gt;Dir&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;tmpdir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;feed.&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="constant"&gt;MD5&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;md5&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;fragment&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;
    &lt;span class="keyword"&gt;begin&lt;/span&gt;
      &lt;span class="ident"&gt;response&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;open&lt;/span&gt; &lt;span class="ident"&gt;filename&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="constant"&gt;Marshal&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;load&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;parse&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  	&lt;span class="keyword"&gt;rescue&lt;/span&gt;   
      &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;validate_data&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;fragment&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@failure&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; could not access w3 validator to validate the feed.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;open&lt;/span&gt; &lt;span class="ident"&gt;filename&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;w+&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="constant"&gt;Marshal&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dump&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
  	&lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;valid?&lt;/span&gt;   
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;description&lt;/span&gt;
    &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;be valid xhtml&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;failure_message&lt;/span&gt;
   &lt;span class="attribute"&gt;@failure&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; expected xhtml to be valid, but validation produced these errors:&lt;span class="escape"&gt;\n&lt;/span&gt; &lt;span class="expr"&gt;#{@message}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;negative_failure_message&lt;/span&gt;
    &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; expected to not be valid, but was (missing validation?)&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;private&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;validity_checks_disabled?&lt;/span&gt;
      &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;NONET&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;be_valid_feed&lt;/span&gt;
  &lt;span class="constant"&gt;BeValidFeed&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I saved this as spec/be_valid_feed.rb&lt;/p&gt;

&lt;p&gt;And in a view or controller spec, I can include this file, and test a response with:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;response.should be_valid_feed&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you use this in a controller spec, you will need to tell RSpec to integrate_views, or you won't have much of a feed to check.  If you use nested example groups, integrate_views needs to be inside the inner group.&lt;/p&gt;

</description>
      <pubDate>Fri, 08 Feb 2008 10:38:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:0b27f7ba-403b-48c5-9833-2fd2b5d73fa8</guid>
      <author>Rick DeNatale</author>
      <link>http://talklikeaduck.denhaven2.com/articles/2008/02/08/validating-rss-feeds-with-rspec</link>
      <category>ruby</category>
      <category>best_practices</category>
      <category>rails</category>
      <category>Rspec</category>
      <category>rss</category>
      <category>validation</category>
      <trackback:ping>http://talklikeaduck.denhaven2.com/articles/trackback/486</trackback:ping>
    </item>
    <item>
      <title>"Validating RSS Feeds with RSpec" by Edgar</title>
      <description>&lt;p&gt;Great !
If I got time I&amp;#8217;ll add the matcher to feedvalidator&lt;/p&gt;</description>
      <pubDate>Sat, 09 Feb 2008 17:11:44 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:b69531dc-e67c-4337-ada4-42d92e556838</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2008/02/08/validating-rss-feeds-with-rspec#comment-3142</link>
    </item>
    <item>
      <title>"Validating RSS Feeds with RSpec" by Rick DeNatale</title>
      <description>&lt;p&gt;Tim -&lt;/p&gt;


	&lt;p&gt;Yes generally I prefer Atom, however for this gig the rss feed is for consumption by iTunes, and as far as I know that requires RSS.&lt;/p&gt;</description>
      <pubDate>Sat, 09 Feb 2008 15:11:48 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:d816573a-63a3-440e-b69c-00abdfcff527</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2008/02/08/validating-rss-feeds-with-rspec#comment-3141</link>
    </item>
    <item>
      <title>"Validating RSS Feeds with RSpec" by Ben Mabey</title>
      <description>&lt;p&gt;Thanks Rick!  This will be handy next time I do a feed.&lt;/p&gt;</description>
      <pubDate>Fri, 08 Feb 2008 13:02:56 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:4686254a-4399-42c5-9392-1f1535a320d4</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2008/02/08/validating-rss-feeds-with-rspec#comment-3134</link>
    </item>
    <item>
      <title>"Validating RSS Feeds with RSpec" by Tim Bray</title>
      <description>&lt;p&gt;Next time, generate Atom 1.0 instead.  The validator messages are way more helpful and you get to dodge a bunch of corner cases with enclosures and summaries and categories and so on.&lt;/p&gt;</description>
      <pubDate>Fri, 08 Feb 2008 12:49:56 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:8dd365d4-29d1-437c-b180-a7a5f034d234</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2008/02/08/validating-rss-feeds-with-rspec#comment-3133</link>
    </item>
  </channel>
</rss>
