<?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: Refactoring With Continuous Testing - a Guided Tour</title>
    <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>In Ruby, it's not the dog, it's the tricks!</description>
    <item>
      <title>Refactoring With Continuous Testing - a Guided Tour</title>
      <description>&lt;div style='width:180; float:left; text-align:right; font-size:xx-small; border-width:1px; border-color:#444444; border-style:solid; padding:3px; margin-bottom:30px; margin-right:30px;'&gt;&lt;img src="http://talklikeaduck.denhaven2.com/files/2007-10-11_davidcensoredonrefrigerator.jpg" alt="DavidCensoredOnRefrigerator" height="240" width="180" title="Photo by Flickr user melomane, cc-by-nc-sa"&gt;&lt;br/&gt;
&lt;a href='http://flickr.com/photos/melomane/333546498/'&gt;Refrigerator Censorship&lt;/a&gt;
&lt;br/&gt;&amp;copy;
&lt;a href='http://flickr.com/people/melomane'&gt;&lt;/a&gt;
&lt;br/&gt;&lt;a href='http://creativecommons.org/licenses/by-nc-nd/2.0/'&gt;&lt;img src='http://i.creativecommons.org/l/by-nc-nd/2.0/80x15.png' title='used under a Creative Commons Attribution-NonCommercial-NoDerivs License' width='80' height='15' border='0'/&gt;&lt;/a&gt;
&lt;/div&gt;

Today on &lt;a href="http://www.rubyfleebie.com/rubyize-this-3rd-edition/"&gt;Ruby Fleebie&lt;/a&gt;, Frank poses some ruby code to be "rubyized." I took this as an opportunity to do a little exposition of re-factoring under the watchful eye of 
&lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;autotest.&lt;/a&gt;
&lt;p&gt;So I've taken Frank's code and run it through the re-factoring machine several times.  A word to the 'squeamish' because of the use case, there are a few words in the code which some folks, and spam filters might find mildly offensive, but we're all adults here right?&lt;/p&gt;
&lt;p&gt;Now I've got to admit that I really came up with the final solution pretty quickly after seeing the blog post, but then I went back and eased up to it for pedagogical purposes.  So if you're in the mood, sit down and lets re-factor some ruby!&lt;/p&gt;




&lt;h2&gt;The Original Code&lt;/h2&gt;
&lt;p&gt;Here's Frank's original code:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;def remove_insults(input)
  ctr = 0
  input.each do |word|
    word = word.downcase
    if word == &amp;quot;stupid&amp;quot; || word == &amp;quot;moron&amp;quot; || 
       word == &amp;quot;dumbass&amp;quot; || word == &amp;quot;retard&amp;quot;
      i=0
      word.length.times do
        input[ctr][i,1] = &amp;quot;*&amp;quot;
        i+=1
      end
    end
    ctr += 1
  end
  puts input.join(&amp;quot; &amp;quot;).to_s 
end
remove_insults &amp;quot;you truly are a moron sir!&amp;quot;.split(&amp;quot; &amp;quot;)  &lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
I've split one line (the long if statement) to try to keep the horizontal scrolling down when viewed here.
&lt;h2&gt;Getting Ready for Autotest&lt;/h2&gt;
&lt;p&gt;My first step was to setup a local subversion repository, so that I could go back over my work and tell you about it.  Then I set up a project directory structure and imported it.&lt;/p&gt;
&lt;/p&gt;Autotest want's a particular directory structure, so I set up a project directory with a lib subdirectory to hold the code, and a test directory to hold the testcase.  The code also needs to be structured as a class, so I changed the code a bit and put it in lib/civilizer.rb&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;Civilizer&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.remove_insults&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;ctr&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&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;word&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;word&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;downcase&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;word&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;stupid&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;word&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;moron&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;word&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;dumbass&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;word&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;retard&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;
        &lt;span class="ident"&gt;word&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;times&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
          &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;][&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;]&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;i&lt;/span&gt;&lt;span class="punct"&gt;+=&lt;/span&gt;&lt;span class="number"&gt;1&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;ctr&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;to_s&lt;/span&gt; 
  &lt;span class="keyword"&gt;end&lt;/span&gt;    
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;remove_insults&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a moron sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;split&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;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that I also set this class up so that it has two methods, remove_insults prints the bowdlerized input, while civilize returns it as a string.  This change makes the code testable&lt;/p&gt; 
&lt;p&gt;Now we need a test case, that goes in test/test_civilizer.rb:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;require &amp;quot;test/unit&amp;quot;

require &amp;quot;civilizer&amp;quot;

class TestCivilizer &amp;lt; Test::Unit::TestCase
  def test_case_name
    assert false    
  end
end&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;More autotest conventions here, we're testing the class Civilizer which lives in lib/civilizer.rb so that autotest can find it, the test case class needs to be TestCivilizer (i.e. the class under test suffixed by Test).&lt;/p&gt;
&lt;p&gt;This initial test case is designed to fail, at this stage I'm just verifying that autotest is happy with my setup, and runs the test when either the code under test or the test case changes.&lt;/p&gt;
&lt;h2&gt;A Real Test Case&lt;/h2&gt;
&lt;p&gt;The next step is to test the actual code. So I changed the test_case_name method (that clever name was generated by the textmate tc snippet by the way, and I didn't change it) to:&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;def &lt;/span&gt;&lt;span class="method"&gt;test_basic_operation&lt;/span&gt;
    &lt;span class="ident"&gt;assert_equal&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a ***** sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a moron sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;split&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;end&lt;/span&gt; &lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When I save this, autotest runs the test and it passes.  Okay, now that we've got a baseline to refactor against, lets start changing code.&lt;/p&gt;
&lt;h2&gt;Our First Change&lt;/h2&gt;
&lt;p&gt;Let's make use of Ruby's Array#include? method and get rid of that if statement with the long chain of ||s:&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;Civilizer&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.remove_insults&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;ctr&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&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;word&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;word&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;downcase&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;%w[&lt;/span&gt;&lt;span class="string"&gt;stupid moron dumbass retard&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="number"&gt;0&lt;/span&gt;
        &lt;span class="ident"&gt;word&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;times&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
          &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;][&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;]&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;i&lt;/span&gt;&lt;span class="punct"&gt;+=&lt;/span&gt;&lt;span class="number"&gt;1&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;ctr&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;to_s&lt;/span&gt; 
  &lt;span class="keyword"&gt;end&lt;/span&gt;    
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;remove_insults&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a moron sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;split&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;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once again, I save this, and autotest automatically confirms that my change didn't break anything.&lt;/p&gt;
&lt;h2&gt;Let's Throw out a Loop or Two&lt;/h2&gt;
&lt;p&gt;That loop to generate the replacement string, seems unwieldy and error prone, let's get rid of it. Here's the new code. I'm focusing on just the method here, the rest of the file hasn't changed:&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;ctr&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
  &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&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;word&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;word&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;downcase&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;%w[&lt;/span&gt;&lt;span class="string"&gt;stupid moron dumbass retard&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;]&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="ident"&gt;ctr&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;to_s&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;Once again we've successfully cleaned up the code a bit, and the test success shows we haven't broken anything.&lt;/p&gt;
&lt;p&gt;While we're looking at loops, how about the one which steps through the words. We're initializing a counter before the loop and incrementing it at the bottom of the loop.  Smells too much like C if you ask me!&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_with_index&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;word&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;word&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;downcase&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;%w[&lt;/span&gt;&lt;span class="string"&gt;stupid moron dumbass retard&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;]&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&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;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;to_s&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;Now this looks more rubyish, but looking at it in retrospect, a better move here would have been to use input.map here with a block which returned either the original nice, or blotted out naughty word, but I didn't think of that at the time.  Of course with my tests I could go back and do that, but I'll leave that as an exercise for the interested reader.&lt;/p&gt;
&lt;p&gt;Anyway, that if statement in the middle is taking up more than it's share of vertical space, let's fix that by using a statement modifier:&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_with_index&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;word&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;word&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;downcase&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;]&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;%w[&lt;/span&gt;&lt;span class="string"&gt;stupid moron dumbass retard&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;word&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;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;to_s&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;This code is getting shorter and tidier as we go, although not as short and tidy as that idea of using map, oh well, but keep with me, it's going to end up real short.&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_with_index&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;word&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;ctr&lt;/span&gt;&lt;span class="punct"&gt;]&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="punct"&gt;%w[&lt;/span&gt;&lt;span class="string"&gt;stupid moron dumbass retard&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;downcase&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;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;to_s&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 don't see any need for a separate assignment statement to downcase the input, so I moved that to the argument of include?, still shorter, but hang on.&lt;/p&gt;
&lt;h2&gt;Time For Some More Tests, and a Shift in Strategy&lt;/h2&gt;
&lt;p&gt;We've gotten to this point without changing any testcase code.  I've been using the testcase to ensure that I haven't changed the code in a way that would make it fail to meet the original 'specification.&lt;/p&gt;
&lt;p&gt;Let's revisit the testcase now. It's a pretty simple test case, how about some variations.  Here's a new test method I added to  test/test_civilizer.rb:&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;def &lt;/span&gt;&lt;span class="method"&gt;test_should_find_multiple_words&lt;/span&gt;
  &lt;span class="ident"&gt;assert_equal&lt;/span&gt;  &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;With all due respect, you must be a ******, you *****!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; 
         &lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&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;With all due respect, you must be a retard, you moron!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;split&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;end&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I've reformatted this a bit to try to keep it narrower for the blog presentation.&lt;/p&gt;
&lt;p&gt;Finally, when I save this, autotest informs me that the test has failed, a little debugging reveals that the problem is those pesky punctuation characters. That String#split method is limiting us to splitting words on spaces.&lt;/p&gt;
&lt;p&gt;Now one way to fix that would be to use a regexp instead of a string as the argument to String#split, but why not just use a regexp to implement the Civilizer, here's the new sleek version of the civilize method:&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;gsub&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\b&lt;/span&gt;(stupid|moron|dumbass|retard)&lt;span class="escape"&gt;\b&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;match&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="keyword"&gt;end&lt;/span&gt;    &lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 
&lt;p&gt;We've gotten the body of the method down to a one liner, &lt;strong&gt;and&lt;/strong&gt; we now properly handle punctuation!&lt;/p&gt;
&lt;h2&gt;The Value of [TB]DD&lt;/h2&gt;
&lt;p&gt;Let's add another test:&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;def &lt;/span&gt;&lt;span class="method"&gt;test_should_ignore_case&lt;/span&gt;
  &lt;span class="ident"&gt;assert_equal&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="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Dumbass!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&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;&lt;strong&gt;Oops!&lt;/strong&gt; this test fails.  Our original tests didn't exercise the use of lowercase in the original code.	 &lt;/p&gt;
&lt;p&gt;This just shows that even test infected developers are human.  Actually if we had developed the original code in a test driven style, we wouldn't have invoked lowercase until we'd written a test case to motivate it.&lt;/p&gt;
&lt;p&gt;Another useful tool to talk about putting in the arsenal is a coverage tool like rcov which profiles your running tests to show how much of the code under test is being exercised, although in this case, it wouldn't have shown the problem&lt;/p&gt;
&lt;p&gt;Anyway, let's fix the problem, we just make the regexp case insensitive:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&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;gsub&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\b&lt;/span&gt;(stupid|moron|dumbass|retard)&lt;span class="escape"&gt;\b&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="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;match&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;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All it took was one little 'i' to fix this bug.  Now autotest is once again showing us the green light.&lt;/p&gt;
&lt;h2&gt;Is the Specification Right?&lt;/h2&gt;
&lt;p&gt;I've been ignoring it all along, but having the caller split the input before giving it to the Civilizer has been bothering me even way before I changed the implementation to using gsub with a regexp, and since then, that code to 'unsplit' it has been sticking out like a sore thumb.  So, after switching from my consultant hat to my client hat and having a quick conference, "we've" agreed to change the interface so that the parameter is a simple string.&lt;/p&gt;
&lt;p&gt;Having agreed to this specification change, let's change the specification.  What do we change? The testcase code of course,  this is the point of test-driven design (or behavior driven design for you &lt;a href="http://www.google.com/url?sa=t&amp;ct=res&amp;cd=1&amp;url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FSapir-Whorf_hypothesis"&gt;Sapir&#8211;Whorf&lt;/a&gt; &lt;a href="http://rspec.rubyforge.org/"&gt;adherents&lt;/a&gt;), whether you call them tests or something else, they are really specifications.&lt;/p&gt;
&lt;p&gt;Without further ado, here's the new spec:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;test/unit&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;civilizer&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;TestCivilizer&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Test&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Unit&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;TestCase&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_find_one_word&lt;/span&gt;
    &lt;span class="ident"&gt;assert_equal&lt;/span&gt;             &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a ***** sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;,&lt;/span&gt; 
          &lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a moron sir!&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;test_should_find_multiple_words&lt;/span&gt;
    &lt;span class="ident"&gt;assert_equal&lt;/span&gt;              &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;With all due respect, you must be a ******, you *****!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; 
           &lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;With all due respect, you must be a retard, you moron!&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;test_should_ignore_case&lt;/span&gt;
    &lt;span class="ident"&gt;assert_equal&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="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Dumbass!&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;end&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I've used slightly unusual formatting here too, which makes it easy to visually compare the expectation with the input.&lt;/p&gt;
&lt;p&gt;In good TDD style, we save this test &lt;strong&gt;before&lt;/strong&gt; we change the code to meet the new specification.  As we expected, autotest shows us a red light.  This is an indication that our changed tests are good.  Now let's change the code:&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;Civilizer&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;self.remove_insults&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&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;def &lt;/span&gt;&lt;span class="method"&gt;self.civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\b&lt;/span&gt;(stupid|moron|dumbass|retard)&lt;span class="escape"&gt;\b&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="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&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="punct"&gt;*&lt;/span&gt; &lt;span class="ident"&gt;match&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="keyword"&gt;end&lt;/span&gt;    
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;remove_insults&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a moron sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Meeting the new spec just required getting rid of the join in civilize, and the split in the final statement which gets executed when we run this code for 'real' as opposed to testing it.  Once again, the feedback from autotest goes green.&lt;/p&gt;
&lt;h2&gt;Test Code Deserves to Be Beautiful Too&lt;/h2&gt;
&lt;p&gt;At this point I stepped back and tidied up the test code.  I wanted to make it DRYer, so I introduced a private method to move the repeated code out of each test case:&lt;/p&gt;
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;test/unit&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;civilizer&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;TestCivilizer&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Test&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Unit&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;TestCase&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;test_should_find_one_word&lt;/span&gt;
    &lt;span class="ident"&gt;assert_civilizes&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a moron sir!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; 
                     &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;you truly are a ***** sir!&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;test_should_find_multiple_words&lt;/span&gt;
    &lt;span class="ident"&gt;assert_civilizes&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;With all due respect, you must be a retard, you moron!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;
                     &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;With all due respect, you must be a ******, you *****!&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;test_should_ignore_case&lt;/span&gt;
    &lt;span class="ident"&gt;assert_civilizes&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Dumbass!&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&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;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;assert_civilizes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;expected&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;assert_equal&lt;/span&gt; &lt;span class="ident"&gt;expected&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Civilizer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;civilize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;input&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;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I don't know about you, but I like the way this code looks.  I made a conscious decision on the order of the arguments. Normally assert* methods first argument is what's expected, but here it's the second.  The decision on the argument ordering was tied up in what to name the method.  I read it as assert_civilizes this [to] that.  I probably spent a full minute or two pondering this, probably the most 'agonizing' decision in this exercise.  If I kept on with this particular 'project' that might change, no code is sacred!&lt;/p&gt; 
&lt;h2&gt;Another Test, Another Spec Change?&lt;/h2&gt;
&lt;p&gt;The last test I wrote was this:&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;def &lt;/span&gt;&lt;span class="method"&gt;test_should_treat_parts_of_words&lt;/span&gt; 
  &lt;span class="ident"&gt;assert_civilizes&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;You, sir are a retarded moron.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;
                   &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;You, sir are a ******ed *****.&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&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've left this as a proposed spec change.  It fails because one of the bad words is not bracketed by word boundaries. This would be easy to make work, just get rid of the two '\b' anchors in the regexp, but I'm not sure that it's a good idea, and neither is the 'client'.  Should 'stupidity' be considered a bad word? or oxymoron?&lt;/p&gt;
&lt;p&gt;I've raised it with the 'client' and I'm still waiting to hear back.&lt;/p&gt;
&lt;h2&gt;What Have We Learned?&lt;/h2&gt;
&lt;p&gt;The whole exercise, involving some 20 subversion commits, probably took me 30-45 minutes, far less time than writing this article.&lt;/p&gt;
&lt;p&gt;I hope that those of you who have stuck this out to the end found it interesting and perhaps useful.  I think it illustrates some key points about test-driven design, rapid iterative development, and dynamic languages:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Having and maintaining testcode as you go provides value by helping ensure that you aren't wandering away from the goal.&lt;/li&gt;
	&lt;li&gt;Good test code, particularly when run automatically, serves as a more powerful substitute for the type checking done by compilers of statically typed languages, it causes both interface and logic errors to cause fast failures which can usually be quickly corrected.&lt;/li&gt;
	&lt;li&gt;Just because code exists doesn't mean it's sacred.  I'm just as happy, probably happier when I throw old code away and replace it with better code, as long as I'm making progress.  Once again testing is a big boon to this goal.&lt;/li&gt;
&lt;/ul&gt;</description>
      <pubDate>Thu, 11 Oct 2007 06:47:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:19c3ac75-cafb-45e8-8901-6a34ceb89e23</guid>
      <author>Rick DeNatale</author>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour</link>
      <category>ruby</category>
      <category>best_practices</category>
      <category>tdd</category>
      <category>bdd</category>
      <category>refactoring</category>
      <category>autotest</category>
      <trackback:ping>http://talklikeaduck.denhaven2.com/articles/trackback/470</trackback:ping>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by James Jeffers</title>
      <description>&lt;p&gt;Thanks to this article, I discovered autotest, which lead to driving over the tracks to UnitRecord and Mocha. Is there no end in sight?&lt;/p&gt;</description>
      <pubDate>Wed, 24 Oct 2007 12:04:18 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:d1edbc7f-a8d8-41fd-9010-4213866e5864</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1654</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by Alex Podaras</title>
      <description>&lt;p&gt;I really enjoyed following this article, thank you for the contribution :)&lt;/p&gt;</description>
      <pubDate>Mon, 15 Oct 2007 08:22:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:ca05d1ff-ff95-4e13-b96e-6b158e405108</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1358</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by John</title>
      <description>&lt;p&gt;Excellent article&amp;#8212;much appreciated.&lt;/p&gt;


	&lt;p&gt;-John&lt;/p&gt;</description>
      <pubDate>Fri, 12 Oct 2007 18:21:39 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:c6a803a1-4fc2-4f98-a0cc-730c81526440</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1330</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by Rick DeNatale</title>
      <description>&lt;p&gt;Pat,&lt;/p&gt;


	&lt;p&gt;Actually I think I prefer having control over commits.&lt;/p&gt;


	&lt;p&gt;I suspect that you&amp;#8217;re comparing SVN to Envy, but think that the overall environmental differences, e.g. file vs method granularity, that I think that manually committing makes sense.&lt;/p&gt;


	&lt;p&gt;And maybe that&amp;#8217;s not the real reason. It&amp;#8217;s really a matter of committing transactions which in general contain multiple changes. Sort of like an Envy configuration, but not quite.&lt;/p&gt;


	&lt;p&gt;Rick&lt;/p&gt;</description>
      <pubDate>Fri, 12 Oct 2007 13:07:14 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:6982c83f-b4b5-4184-ab4f-136b7954ecaa</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1324</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by FrankLamontagne</title>
      <description>&lt;p&gt;Wow&amp;#8230; great article Rick.&lt;/p&gt;


	&lt;p&gt;I really liked the step-by-step approach that you took.&lt;/p&gt;


	&lt;p&gt;And thanks for using my article as the starting point!&lt;/p&gt;


	&lt;p&gt;Frank&lt;/p&gt;</description>
      <pubDate>Thu, 11 Oct 2007 19:18:24 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:0dd0d759-1455-425a-91d0-e852ecd05d82</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1317</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by Patrick Mueller</title>
      <description>&lt;p&gt;Great stuff Rick.  Thanks for the teaching, as always.&lt;/p&gt;


	&lt;p&gt;Two notes:&lt;/p&gt;


	&lt;p&gt;- I&amp;#8217;m an anal liner-upper too, and would have definitely lined up the civil/uncivil strings like you did.&lt;/p&gt;


	&lt;p&gt;- Having to do SVN commits seems like overkill.  No way to run in a &amp;#8216;lighter&amp;#8217; mode where it just polls for file changes?  Certainly this sort of behaviour isn&amp;#8217;t going to work for people who like commits to be more coarsely grained.  But not as much of a problem if you were using a distributed, change set based SCM.&lt;/p&gt;</description>
      <pubDate>Thu, 11 Oct 2007 17:19:02 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:abb27783-3064-4a51-929c-a83c1df7e410</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1315</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by James Jeffers</title>
      <description>&lt;p&gt;Thank you very much &amp;#8211; I really enjoyed that!&lt;/p&gt;</description>
      <pubDate>Thu, 11 Oct 2007 15:28:17 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:05440b20-5577-468f-bf2b-a70c5af5e65d</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1313</link>
    </item>
    <item>
      <title>"Refactoring With Continuous Testing - a Guided Tour" by Rick DeNatale</title>
      <description>&lt;p&gt;A few times in the above I talked about reformatting the code slightly (additional line breaks) to make it narrower and minimize the horizontal scroll bars.&lt;/p&gt;


	&lt;p&gt;Well it seems that markup reflows it anyway, so &amp;#8230;&lt;/p&gt;</description>
      <pubDate>Thu, 11 Oct 2007 14:54:59 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:373ae811-f790-4b2e-ad69-a8f28a03ae70</guid>
      <link>http://talklikeaduck.denhaven2.com/articles/2007/10/11/refactoring-with-continuous-testing-a-guided-tour#comment-1312</link>
    </item>
  </channel>
</rss>
