<?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>Wolfmans Howlings: RSpec testing views for escaped HTML</title>
    <link>http://blog.wolfman.com/articles/2007/07/06/rspec-testing-views-for-escaped-html</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>A programmers Blog about Ruby, Rails and a few other issues</description>
    <item>
      <title>RSpec testing views for escaped HTML</title>
      <description>&lt;p&gt;For my social networking site &lt;a href="http://snowdogsr.us"&gt;snowdogsr.us&lt;/a&gt; I
decided to escape all user input that gets displayed. I know people
like to trick out their profiles with HTML but I want to avoid the
various hacks that it allows.&lt;/p&gt;

&lt;p&gt;So thinking I had done a good job of using h everywhere I output user
input fields, I decided to see if I could actually test this with
RSpec view tests.&lt;/p&gt;

&lt;p&gt;I recently switched to &lt;a href="http://rspec.rubyforge.org"&gt;RSpec&lt;/a&gt; for my
testing needs, its cool :)&lt;/p&gt;

&lt;p&gt;One thing it does is isolate the various things for testing using built
in mocking, and views can be entirely tested standalone without
accessing a model or a controller.&lt;/p&gt;

&lt;p&gt;So how would it do testing for escaped user input I wondered?&lt;/p&gt;

&lt;p&gt;Very well actually.&lt;/p&gt;

&lt;p&gt;An example is worth a thousand words, so here is my RSpec for my home
page.&lt;/p&gt;

&lt;p&gt;BTW I found about 4 places where embedded HTML in user input was
bleeding through, so it was well worth the effort.&lt;/p&gt;

&lt;p&gt;So this goes in spec/views/home/home_spec.rb...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should escape all user input&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@place&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Place&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;place name&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:location&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;place location&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:tag_list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;place taglist &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:rated?&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@event&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Event&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;event name&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:where&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;event where&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:tag_list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;event taglist &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:date_time&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:hosted_by&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Event host&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="attribute"&gt;@post&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Input&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:input&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;input body &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:tag_list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;post taglist &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:updated_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:created_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:created_by&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;post created by person&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:rated?&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@picture&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Picture&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:public_filename&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;filename&amp;lt;b&amp;gt;.png&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@pictures&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@picture&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@pet&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Pet&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;pet name&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:owned_by&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;pet owner &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:breed&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;breed &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:description&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;pet description &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:neutered&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:gender&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;M&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:pictures&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@pictures&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:owned_by?&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@post&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@events&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@event&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@places&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@place&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@top_places&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@place&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_pets&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@pet&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;

    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Comment&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:user&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:created_at&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:comment&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;comment body &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="attribute"&gt;@comments&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_receive&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:comments&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@comments&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Posts&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@events&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Events&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@places&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Places&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;

    &lt;span class="attribute"&gt;@top&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="attribute"&gt;@top&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@top_places&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Hot Places&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;

    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:new_stuff&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt;
    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:top&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@top&lt;/span&gt;
    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:new_pets&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@new_pets&lt;/span&gt;

    &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/home/logged_in&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;   
    &lt;span class="comment"&gt;#puts excerpt(response.body, &amp;quot;&amp;lt;b&amp;gt;&amp;quot;)&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;should_not&lt;/span&gt; &lt;span class="ident"&gt;have_text&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Its quite complex as the home page renders a lot of summaries of the
various lists I have.&lt;/p&gt;

&lt;p&gt;First I mock the models that are called, and stub out the calls that
are made to them. I force them all to return an embedded &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; which I
don't use anyway, and with the new CSS oriented web styles shouldn't
be used in HTML anyway.&lt;/p&gt;

&lt;p&gt;Then I just test that &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; does not appear anywhere. If I have
correctly used h to escape all the inputs then it should be rendered
as &lt;code&gt;&amp;amp;lt;b&amp;amp;gt;&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;response.should_not have_text(/&amp;lt;b&amp;gt;/)&lt;/code&gt; should do that test.&lt;/p&gt;

&lt;p&gt;One cool thing is the mocking will tell you if any new inputs (ie
calls to model attributes) have been added, or if you have forgotten
any. So this should keep you honest in the future if you add new
attributes that need escaping.&lt;/p&gt;

&lt;p&gt;The &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;assigns[:new_stuff] = @new_stuff
assigns[:top] = @top
assigns[:new_pets] = @new_pets
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sets the assigns to the variables that my view uses,simulating what
the controller would pass in.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mock_model&lt;/code&gt; calls at the top also use a shortcut to define all
the attributes that get called, and what they return. You can also
explicitly do this...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@post.should_receive(:comments).and_return(@comments)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you read the RSpec docs you can see that you can also test for
parameters passed in, how many times it is called and various other
nice things.&lt;/p&gt;

&lt;p&gt;I added this snippet taken from the rails helpers to aid in finding
any errant HTML that bleads through. (I'm not sure how to call it
from the RSpec so I just copied the code into a private method).&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;
 &lt;span class="ident"&gt;private&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;excerpt&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;phrase&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;radius&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;100&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;excerpt_string&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;...&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;phrase&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt; &lt;span class="keyword"&gt;then&lt;/span&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;phrase&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Regexp&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;escape&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;phrase&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;found_pos&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;chars&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;(&lt;span class="expr"&gt;#{phrase}&lt;/span&gt;)&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;
      &lt;span class="ident"&gt;start_pos&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt; &lt;span class="ident"&gt;found_pos&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="ident"&gt;radius&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;max&lt;/span&gt;
      &lt;span class="ident"&gt;end_pos&lt;/span&gt;   &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt; &lt;span class="ident"&gt;found_pos&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;phrase&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;chars&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;radius&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;chars&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;min&lt;/span&gt;

      &lt;span class="ident"&gt;prefix&lt;/span&gt;  &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;start_pos&lt;/span&gt; &lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;excerpt_string&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;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;postfix&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;end_pos&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;chars&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;excerpt_string&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;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

      &lt;span class="ident"&gt;prefix&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;chars&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;start_pos&lt;/span&gt;&lt;span class="punct"&gt;..&lt;/span&gt;&lt;span class="ident"&gt;end_pos&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;strip&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;postfix&lt;/span&gt;
    &lt;span class="keyword"&gt;else&lt;/span&gt;
      &lt;span class="constant"&gt;nil&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and you can see the call that shows me where the errant &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; is...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puts excerpt(response.body, "&amp;lt;b&amp;gt;")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I also have some setup code that handles the login and log out
mocking, but I'll leave that for the end user to sort out ;)&lt;/p&gt;

&lt;p&gt;So I think this will make sure that now and in the future this
particular view will not bleed user input HTML.&lt;/p&gt;

&lt;p&gt;Once I did the complex one above the rest of the views were much easier and quicker to implement.
Here is an example of a really simple one...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should escape all user input&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@person&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Person&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;person name &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:first_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;person first name &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:last_name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;person last name &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:alias&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;person alias &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:show_gender&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Male&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:about_me&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;about &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:updated_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:created_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:pets&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[])&lt;/span&gt;    

    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:person&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@person&lt;/span&gt;

    &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/people/show&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&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;should_not&lt;/span&gt; &lt;span class="ident"&gt;have_text&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Couldn't be much simpler, but I found one place where I was not escaping the HTML!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/rspec" rel="tag"&gt;&lt;/a&gt;
&lt;a href="http://technorati.com/tag/view+testing" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 06 Jul 2007 18:22:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:750e90a2-3017-4858-971a-be0121ae78b6</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2007/07/06/rspec-testing-views-for-escaped-html</link>
      <category>RSpec</category>
      <category>Rails</category>
      <category>rails</category>
      <category>rspec</category>
      <category>escapinghtml</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/331</trackback:ping>
    </item>
    <item>
      <title>"RSpec testing views for escaped HTML" by Henrik N</title>
      <description>&lt;p&gt;Thanks for the idea &#8211; implemented something similar.&lt;/p&gt;

&lt;p&gt;You can probably do something like this to get around re-implementing excerpt:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;puts "Unescaped user data here: "  + response.template.excerpt(response.body, "&amp;lt;b&amp;gt;")
&lt;/code&gt;&lt;/pre&gt;</description>
      <pubDate>Mon, 14 Jan 2008 07:59:41 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:e66c718c-316a-40f3-9d8c-3fe521fc9214</guid>
      <link>http://blog.wolfman.com/articles/2007/07/06/rspec-testing-views-for-escaped-html#comment-194</link>
    </item>
    <item>
      <title>"RSpec testing views for escaped HTML" by Tony</title>
      <description>&lt;p&gt;So this still requires a setup and a spec for &lt;em&gt;every view&lt;/em&gt;. Could get quite complicated for heavier pages, as the home example demonstrates.&lt;/p&gt;

&lt;p&gt;Would be nice if HAML just escaped everything on its own in the first place.&lt;/p&gt;</description>
      <pubDate>Wed, 01 Aug 2007 11:33:53 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:eeada837-564a-4d10-b10f-a0a683fb1314</guid>
      <link>http://blog.wolfman.com/articles/2007/07/06/rspec-testing-views-for-escaped-html#comment-125</link>
    </item>
    <item>
      <title>"RSpec testing views for escaped HTML" by wolfmanjm</title>
      <description>&lt;p&gt;Thats a good suggestion. The reason it is in one behavior is there is only one render call, and I try to test the render as it would be called in real life with lots of input.&lt;/p&gt;

&lt;p&gt;However for the purpose of this specific test, you are right it would be easier to track down the problem if I tested each object separately and called the render three times instead of once.&lt;/p&gt;

&lt;p&gt;However it wasn't really hard to track down the problems when the &lt;code&gt;puts excerpt...&lt;/code&gt; was enabled as it showed exactly where the error was.&lt;/p&gt;</description>
      <pubDate>Wed, 18 Jul 2007 21:52:25 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:86a95538-0abe-4c49-81e8-b01aeea827e9</guid>
      <link>http://blog.wolfman.com/articles/2007/07/06/rspec-testing-views-for-escaped-html#comment-105</link>
    </item>
    <item>
      <title>"RSpec testing views for escaped HTML" by zubari</title>
      <description>&lt;p&gt;You might find it useful to break down that rather large specification into separate blocks for each of the models you use so that it is easier for you to identify problems if they do occur. In its current state, if you DID forget to escape the html, it wouldn't be obvious where the problem was occurring. 
A possible strategy would be to mock the models in a "before" block, and then add the stubbing afterwards in separate behaviours.&lt;/p&gt;</description>
      <pubDate>Wed, 18 Jul 2007 20:45:59 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:8316d905-ddc9-4aa9-a5e4-8945d47d1ef1</guid>
      <link>http://blog.wolfman.com/articles/2007/07/06/rspec-testing-views-for-escaped-html#comment-104</link>
    </item>
  </channel>
</rss>
