<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" ><channel><title>Pathfinder Software &#187; Noel Rappin</title> <atom:link href="http://pathfindersoftware.com/author/noel-rappin/feed/" rel="self" type="application/rss+xml" /><link>http://pathfindersoftware.com</link> <description>The Fastest Way to Launch Successful Software</description> <lastBuildDate>Thu, 19 Jan 2012 16:31:04 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>A Pair of Kings Beats A Single Ace: Pair Programming, Agile Rails, and You</title><link>http://pathfindersoftware.com/2009/09/a-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you/</link> <comments>http://pathfindersoftware.com/2009/09/a-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you/#comments</comments> <pubDate>Fri, 25 Sep 2009 14:59:01 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[agile]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=4158</guid> <description><![CDATA[A lot of pair programming chatter this week. Starting with a New York times article describing pair programming at Hashrocket. It&#8217;s an interesting article, with a tone that could be described as &#8220;anthropologist describing the strange, yet quaint customs of the native tribe&#8221; Obie Fernandez followed up with a list of 10 reasons why pairing ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><p><img src="http://pathfindersoftware.com/wp-content/uploads/C34FF759-31EE-407B-A9E2-0A46112137351.jpg" alt="C34FF759-31EE-407B-A9E2-0A4611213735.jpg" border="0" width="200" height="211" class="right"/></p><p>A lot of pair programming chatter this week. Starting with <a href="http://www.nytimes.com/2009/09/20/jobs/20pre.html?_r=2">a New York times article describing pair programming at Hashrocket</a>. It&#8217;s an interesting article, with a tone that could be described as &#8220;anthropologist describing the strange, yet quaint customs of the native tribe&#8221;</p><p>Obie Fernandez <a href="http://blog.obiefernandez.com/content/2009/09/10-reasons-pair-programming-is-not-for-the-masses.html">followed up with a list of 10 reasons why pairing doesn&#8217;t work in most cases</a>. It&#8217;s actually a list of the things that Hashrocket does to support pairing, although entries like &#8220;2. Most software developers just don&#8217;t want to work that hard&#8221; and &#8220;1. Most software shops don&#8217;t really care about excellence&#8221; do have a certain, &#8220;aren&#8217;t we great&#8221; vibe to them, causing Mike Gunderloy <a href="http://twitter.com/MikeG1/status/4314308701">to dryly observe</a>: &#8220;Funny, Extreme Programming Explained never said anything about fancy hw or being awesome as a prerequisite for pair programming.&#8221;</p><p>C&#8217;mon Mike &#8212; everybody knows that being awesome is a prerequisite for <em>everything</em> in XP.</p><p>Josh Susser adds <a href="http://blog.hasmanythrough.com/2009/9/23/pair-programming-isnt-right-for-all-projects">that pair programming isn&#8217;t right for all projects</a>, particularly projects that have long compile times that force the pair to stare blankly at the screen.</p><p>I&#8217;d also add this <a href="http://www.twit.tv/floss87">interview with Kent Beck</a> because a) every programmer could use some more Kent Beck in their life and b) because he talks about XP as being concerned with the the social context of programmers, with pairing being a part of that.</p><p>Now you are caught up. Here&#8217;s the part where I talk.</p><p><span id="more-4158"></span>I&#8217;ve had a running debate with Dietrich for two years now. He thinks that Pair Programming is the number one most important part of an agile team. I think it&#8217;s testing. That said, I do think pairing can be a nutritious part of your agile breakfast. But it&#8217;s tricky to do right and easy to do wrong.</p><p><strong>Pairing is for the long haul</strong>. Like pretty much everything in the Agile toolkit, the real gain in pairing happens over time and is hard to quantify because it&#8217;s the absence of friction later in the project. Code is cleaner and easier to change. System knowledge is more distributed, so you are less likely to freak out when Fred catches H1N1 a week before.</p><p>Actually, I&#8217;d describe most of the XP practices as &#8220;we thought we were trading short-term productivity for long-term productivity, then found that we got short-term gains as well.&#8221; Certainly can apply to TDD.</p><p><strong>Project size and make up matters</strong>. A team of three developers, for example, is a challenge for an always-pairing environment. Even a team of two is a problem &#8212; some tasks really don&#8217;t lend themselves to pairing. If people on the team also has non-developer responsibilities, that&#8217;s another challenge.</p><p><strong>Some people really don&#8217;t like it</strong>. And I think it&#8217;s glib to say &#8220;those people are lazy&#8221; or &#8220;those people don&#8217;t care about excellence&#8221;. Even I find pair programming really tiring, but in a &#8220;I worked hard today, plus I had to deal with another person all day.&#8221; It&#8217;s not unusual for programmers to be very strong Meyers-Briggs introvert-intuitives, and it&#8217;s not surprising that personality type would find pairing a challenge. It&#8217;s not insurmountable, but you do need to structure the pairing with people in mind &#8212; the Hashrocket 25 minutes on/ 5 minutes off thing is a good start.</p><p>While I&#8217;m here, I think you can really overstate the &#8220;people work harder with somebody next to them&#8221; thing. Pairing is great and helpful because of the continual review and talking about what you are doing. Conceiving pairing as a way to use peer pressure to keep your coders off of Twitter is, I submit, not very much in keeping with the spirit of the Agile Manifesto.</p><p><strong>The physical layout is really important</strong>. A lot of Obie&#8217;s piece is about this. I just want to emphasize first that pairing is a lot louder than solo programming, and this can be an issue in open office environments, and also that pairing really does work better on a neutral environment that isn&#8217;t either developer&#8217;s home machine.</p><p><strong>Pairing is hard to reconcile with offsite practices</strong> And I don&#8217;t mean just when you&#8217;re working with six guys in Krakow, but also any kind of work at home, flexible hours, programmer-friendly kind of time structure is a real challenge to integrate with pairing.</p><p>Hmm&#8230; A lot of challenges, not much guidance. Which matches my feelings. When put in the right context, pairing is fantastic. But the context can be elusive, even for people with the best of intentions and talent.</p><p>Would this have been a better blog post if I had paired with a co-worker?<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;linkname=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fa-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you%2F&amp;title=A%20Pair%20of%20Kings%20Beats%20A%20Single%20Ace%3A%20Pair%20Programming%2C%20Agile%20Rails%2C%20and%20You" id="wpa2a_2">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/09/a-pair-of-kings-beats-a-single-ace-pair-programming-agile-rails-and-you/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Corners of the Rubyverse: RVM and MacRuby</title><link>http://pathfindersoftware.com/2009/09/corners-of-the-rubyverse-rvm-and-macruby/</link> <comments>http://pathfindersoftware.com/2009/09/corners-of-the-rubyverse-rvm-and-macruby/#comments</comments> <pubDate>Fri, 18 Sep 2009 16:59:18 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=4047</guid> <description><![CDATA[Please continue reading after the next sentence. I installed Snow Leopard a couple of weeks ago. Wait &#8212; don&#8217;t stop reading. This isn&#8217;t a post about how to install MySQL or a post about whether or not Snow Leopard is the Greatest Thing Ever. There are plenty of other places on the Internet where you ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><p><img src="http://pathfindersoftware.com/wp-content/uploads/C2418006-9AF1-4724-A2CF-27460658A1151.jpg" alt="C2418006-9AF1-4724-A2CF-27460658A115.jpg" border="0" width="300" height="300" class="right"/></p><p>Please continue reading after the next sentence.</p><p>I installed Snow Leopard a couple of weeks ago.</p><p>Wait &#8212; don&#8217;t stop reading. This isn&#8217;t a post about how to install MySQL or a post about whether or not Snow Leopard is the Greatest Thing Ever. There are plenty of other places on the Internet where you can get that information.</p><p>I wanted to talk about two cool corners of the Ruby universe that I started using as a result of my Snow Leopard installation: MacRuby and RVM<span id="more-4047"></span><br /><h3>RVM: Ruby Version Manager</h3><p>One of the Ruby changes in Snow Leopard was an upgrade of the system Ruby version to 1.8.7. All fine, except that one of the projects I work on is on an older version of Rails that is not compatible. Clearly, I needed to get an 1.8.6 Ruby on my machine, ideally without messing up the system installation that other applications are using.</p><p>Enter <a href="http://rvm.beginrescueend.com">Ruby Version Manager</a> (RVM), a handy command line tool for installing and managing multiple Ruby interpreters on your system. In theory, usage is as simple as this:</p><pre style="white-space: pre !important;">
$ gem install rvm
$ rvm-install
</pre><p>At this point RVM asks you to make a slight change to your shell files. Then just this:</p><pre style="white-space: pre !important;">
$ rvm use 1.8.6
</pre><p>&#8220;But you didn&#8217;t have 1.8.6&#8243;, I hear you cry. True. RVM will go out, download, compile, and install Ruby 1.8.6 to my local machine without interfering with anything else on the system (it installs in ~/.rvm). You can install Ruby 1.8.x, Ruby 1.9.x, Ruby Enterprise, JRuby, Rubinius, and (I think) MacRuby.</p><p>(Okay &#8212; that&#8217;s the ideal. In practice, it took a little bit of system tweaks and command line tweaks. <a href="http://afreshcup.com/2009/09/02/migrating-to-snow-leopard-for-rails-development-a-definitive-guide/">Mike Gunderloy&#8217;s Snow Leopard guide</a> was helpful here. Although I&#8217;ve been able to get all the main Ruby versions to install, I&#8217;m still struggling some of the more esoteric version. Once installation is done, the command line interface is really good and things just work.)</p><p>Now, the Ruby in my shell is the new 1.8.6. But any other running terminal shells are unaffected &#8212; especially helpful if you are working on multiple projects. Each Ruby maintains its own Gem listing, although there&#8217;s work ongoing to make it easy to share gems.</p><p>To get back to the system Ruby, just</p><pre style="white-space: pre !important;">
rvm use system
</pre><p>You can also specify any Ruby version as the default.</p><p>This is outstandingly cool, not just to solve my problem, but also as an easy way to create a harness to test your Ruby program or library against multiple Ruby setups. There are other useful features about setting different versions and managing shells, check it out.</p><h3>MacRuby</h3><p>Here&#8217;s the thing. For the last several years I&#8217;ve had this script that communicates with iTunes via AppleScript, and creates a bunch of random playlists according to criteria that is a bit more complex that an iTunes smart playlist. For instance, it can create a playlist made up of two-song blocks by the same artist. Okay, it&#8217;s wildly overdone, but I like it.</p><p>It broke in Snow Leopard. I don&#8217;t know why. It seems like the Scripting Bridge framework occasionally decides to go out for a cup of coffee, and my script times out.</p><p>This seemed like as good a time as any to investigate MacRuby. <a href="http://www.macruby.org">MacRuby</a> is an implementation of Ruby in Mac OS X Objective-C. Unlike a lot of hybrid language/vm tools, MacRuby gives you direct access to the native objects. So, if you ask for a string, you get an object that acts as both a Ruby string and a Cocoa NSString, responding to methods of either. MacRuby uses Ruby 1.9 key/value arguments to translate Objective-C method names.</p><pre style="white-space: pre !important;">
[person name];
[person setName:name];
[person setFirstName:first lastName:last];
</pre><p>In Ruby (this example is from a tutorial on the MacRuby site)</p><pre style="white-space: pre !important;">
person.name
person.setName(name)
person.setFirstName(first, lastName:last)
</pre><p>Unlike regular Ruby, the order of the keyword arguments must match the Cocoa method selector.</p><p>For most people, this allows writing Cocoa applications in Ruby, including integration with XCode and Interface Builder.</p><p>That&#8217;s extremely useful, and I plan on trying it soon. For my purposes, the point is that it uses the Scripting Bridge directly, and I hoped that would allow it to bypass whatever weirdness was breaking my original script. (The original script was in Python, but I had a 75% functional Ruby version that I never actually built the I/O on, so it was largely a matter of learning the MacRuby way to communicate with iTunes.) Here&#8217;s a sample, cobbled together from various parts of the script:</p><pre style="white-space: pre !important;">
def itunes
  @itunes ||= SBApplication.applicationWithBundleIdentifier(
    "com.apple.itunes")
end

def library
  @library ||= itunes.sources.objectWithName("Library")
end

def all_music
  @all_music ||= library.userPlaylists.objectWithName("Music")
end

all_music.fileTracks.each_with_index do |track, index|
  # stuff here
end

def create_itunes_playlist
  playlist = itunes.classForScriptingClass(
      "playlist").alloc.initWithProperties(
      {'name' => name})
  library.playlists.addObject(playlist)
  playlist
end

#chosen_tracks are my object wrappers around Cocoa
#itunes_track is the actual cocoa object
playlist = create_itunes_playlist
chosen_tracks.each_with_index do |track, index|
  track.itunes_track.duplicateTo(playlist)
end
</pre><p>Overall, everything works as advertised (it seems as though MacRuby is better able to deal with whatever happens to cause the Scripting Bridge to take a nap. I&#8217;m using the pre-release MacRuby 0.5, so there&#8217;s the occasional feature glitch (gem installation is a little dicey, for example). But the MacRuby team is actively, even furiously, pushing forward, and this looks like it&#8217;ll be very useful, very soon.<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;linkname=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fcorners-of-the-rubyverse-rvm-and-macruby%2F&amp;title=Corners%20of%20the%20Rubyverse%3A%20RVM%20and%20MacRuby" id="wpa2a_4">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/09/corners-of-the-rubyverse-rvm-and-macruby/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>WindyCityRails: My Presentation Checklist</title><link>http://pathfindersoftware.com/2009/09/windycityrails-my-presentation-checklist/</link> <comments>http://pathfindersoftware.com/2009/09/windycityrails-my-presentation-checklist/#comments</comments> <pubDate>Fri, 11 Sep 2009 16:14:16 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[Technologies and Platforms]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=4009</guid> <description><![CDATA[WindyCityRails 2008 Sometimes I write these just for me. As I&#8217;ve mentioned a couple of times, tomorrow I&#8217;ll be speaking at WindyCityRails, and I need a checklist of all things I don&#8217;t want to forget, and all thing things I want to do to make the talk great. Preparing the Talk Figure out what you ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"><a href=""><img src="http://pathfindersoftware.com/wp-content/uploads/2892206860_20c318941b_m.jpg" alt="WindyCityRails 2008" border="0" width="" height="" class="right"/></a><br clear="all"/><span class="right" style="font-size: smaller"><a href="http://www.windycityrails.org/">WindyCityRails 2008</a></span></div><p>Sometimes I write these just for me.</p><p>As I&#8217;ve mentioned a couple of times, tomorrow I&#8217;ll be speaking at <a href="http://www.windycityrails.org">WindyCityRails</a>, and I need a checklist of all things I don&#8217;t want to forget, and all thing things I want to do to make the talk great.<br /> <span id="more-4009"></span><br /><h3>Preparing the Talk</h3><p>Figure out what you want to say first. The outline view in PowerPoint and Keynote is a great way to organize what you want to say without worrying about how it looks.</p><p>Determine a small number of things that you think are key items that you want the audience to walk away with. Mention them at the start, summarize them at the end.</p><p>The classic advice is not to put too much text on your slides, and avoid reading the text directly. A pragmatic way to manage this is to set your body text font pretty large (I use 44 point fonts for my body text if I can get away with it), and break up any slide that threatens to overrun.</p><p>That said, you can make the opposite mistake as well &#8212; I find it tiring to go to a talk where all the slides are two-words and a non-sequitur picture. People may be viewing just your slides online, they need a little bit of context. Even people in the room often need a bit of context on the slides to help follow what you are saying.</p><p>Building up the slides during the talk point by point using animation can be a good way to keep from reading too much text on the slides. And it can help time a &#8220;punch line&#8221; by keeping it from the audience until you are ready. But don&#8217;t use it everywhere. (I&#8217;ve always wanted to give a talk that used bullet points like Colbert does in The Word segment, as ironic commentary. But that&#8217;s very hard, and I&#8217;m not sure it would work.)</p><p>I do like to separate off major sections of the talk with title-only slides. I&#8217;ve started tying these sections together by putting images on the title slide, and carrying those images in the background of the slides in the section at something like 20% opaque. It makes it easier for people in the audience to follow along.</p><p>Keep the number of moving parts to a minimum &#8212; rather than switch to an editor to show code, copy the code into your slides. TextMate has a &#8220;Copy to RTF&#8221; bundle that preserves syntax highlighting, or take a screen shot.</p><p>Similarly, try to avoid having to run code during your talk. (Unless the interactivity is the point, as in a longer workshop).</p><p>Try not to depend on Internet access for a demo, you never know how flakey the Internet is going to be at a venue. At the very least, have a bunch of screenshots in hand as a plan B.</p><p>Practice. Do a dry run by yourself. If you see something wrong in your slides or missing in your talk, write it down. But do try and get a clean dry run at some point, so you get a sense of how long the talk is.</p><p>If possible, practice in front of other people. Nearly every talk that somebody at Pathfinder gives has a practice run as a company brown bag. This is invaluable feedback as to what is interesting in your talk and what is not.</p><p>In many situations, your last slide will be up for a while while you answer questions or something. Put something useful on there, generally either a summary or places to go for more information.</p><h3>Doing the Talk</h3><p>Make sure you bring everything you need. Laptop. Power Cord. Monitor Adapter. Remote Control. Don&#8217;t assume that anything will be as you expect in the actual setup. As I type this I&#8217;m going to put my monitor adapter in my backpack. Put a copy of your talk on a thumb drive. Put a copy online using Dropbox or Gmail it to yourself.</p><p>If at all possible, scope out the space well before your talk. Where will you stand? Will you be able to see your own slides? Will you need a remote, or will you need to stand in front of your presentation machine?</p><p>Try and set up early if you can (if you are part of a conference program, you&#8217;re limited because there&#8217;s a previous speaker). Still, the less the audience sees you fumbling with cables the better off you are. It helps if you can check to see if the display resolution matches your slides.</p><p>Before the talk, shut down as many programs as you can on your laptop. Especially shut down notifiers &#8212; you don&#8217;t want an IM from your mom popping up in the middle.</p><p>I use a Mac utility called <a href="http://lightheadsw.com/caffeine/">Caffeine</a> to keep my computer from going to sleep in the middle of the talk, because it&#8217;s annoying when that happens.</p><p>The point of all this is to reduce the amount of things that can go wrong so that you can be as relaxed and have fun during the talk.</p><p>Getting started can be awkward. Sometimes it helps to get the audience involved by asking a question or two right off the bat &#8212; this can also help you gauge how much the audience already knows about your topic. It can also be an unobtrusive way to make sure that everybody can hear you.</p><p>At the end, if there&#8217;s time, take questions. Get in the habit of repeating the question before you answer it. There&#8217;s a good chance that most of the audience didn&#8217;t hear the question. If you are miked and being recorded, the recording probably won&#8217;t pick up the question unless you repeat it.</p><p>Stick around afterword if you can, often there will be one or two more people who have questions.<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;linkname=WindyCityRails%3A%20My%20Presentation%20Checklist" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fwindycityrails-my-presentation-checklist%2F&amp;title=WindyCityRails%3A%20My%20Presentation%20Checklist" id="wpa2a_6">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/09/windycityrails-my-presentation-checklist/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Ask A Rails Tester Person</title><link>http://pathfindersoftware.com/2009/09/ask-a-rails-tester-person/</link> <comments>http://pathfindersoftware.com/2009/09/ask-a-rails-tester-person/#comments</comments> <pubDate>Fri, 04 Sep 2009 15:39:20 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[tdd]]></category> <category><![CDATA[Test Driven Development]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3945</guid> <description><![CDATA[Ask Mr. Lizard, from Jim Henson&#8217;s Dinosaurs It&#8217;s time to play &#8220;Ask A Tester Person&#8221;, where I answer questions that I&#8217;ve gotten via email or otherwise about Rails Testing topics. If you have a question for Ask A Tester Person, send it to railsprescriptions at gmail.com. Before I continue, I want to mention that Pathfinder&#8217;s ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"><img src="http://pathfindersoftware.com/wp-content/uploads/8B461F0C-C372-4689-A81C-146FD44E5EEB1.jpg" alt="Ask Mr. Lizard" border="0" width="" height="" class="right"/><br clear="all"/><br /> <span class="right" style="font-size: smaller">Ask Mr. Lizard, from Jim Henson&#8217;s Dinosaurs</span></div><p>It&#8217;s time to play &#8220;Ask A Tester Person&#8221;, where I answer questions that I&#8217;ve gotten via email or otherwise about Rails Testing topics.</p><p>If you have a question for Ask A Tester Person, send it to railsprescriptions at gmail.com.</p><div style="border: thin solid blue; padding: 5px"> Before I continue, I want to mention that Pathfinder&#8217;s own John McCaffrey and myself will both be presenting at <a href="http://www.windycityrails">WindyCityRails 2009</a>, which is September 12th at the Westin Chicago River North. There are still some seats available for the main conference talks, registration is open until September 10th. So sign up and we&#8217;ll see you there.</div><p>I&#8217;ve got two questions today:<br /> <span id="more-3945"></span><br /><h3>Question 1: I&#8217;m writing too many tests!</h3><blockquote><p>One subject I still have questions<br /> about is how to test authorization in controller tests. In my<br /> applications I&#8217;ve been testing three different user cases for every<br /> controller action, but this leads to, for example:</p><pre style="white-space: pre !important;">
test_edit_by_anonymous_user
test_edit_by_unauthorized_user
test_edit_by_authorized_user
</pre><p>So every action has at least three tests, which obviously means a<br /> *ton* of tests for the entire application. This has always felt like<br /> overdoing it but I haven&#8217;t yet been able to convince myself that just<br /> testing the authorization part of the code is sufficient. If I could<br /> isolate the authorization mechanism for testing that might convince<br /> me, but I&#8217;m not really able to do that (maybe my authorization process<br /> needs to be redesigned?), and I&#8217;m not sure I&#8217;d be convinced<br /> anyway&#8230;how the application responds in all three cases seems like<br /> something that *should* be tested for every action&#8230;</p></blockquote><p>I doubt that your authorization process needs to be redesigned, but then I&#8217;ve never seen your authentication process.</p><p>I&#8217;d recommend a couple of different things to clean this up a bit. On the assumption that unauthorized access has largely the same behavior across the application, you can create a boilerplate test like this example &#8212; the example uses Shoulda, but the basic idea should work in any framework.</p><pre style="white-space: pre !important;">
  self.def should_block_access_for_anonymous_user(*actions)
    actions.each do |action|
      should "block anonymous access for #{action}" do
        logout_current_user
        get action
        assert_redirected_to root_path
      end
    end
	end
</pre><p>Which you would then use as:</p><pre style="white-space: pre !important;">
  should_block_access_for_anonymous_user :edit, :update, :delete
</pre><p>The implementation shown here is probably a little too simplistic for full use (you might need to pass more information than just the action for each request), but the simple version could easily cover the most basic authentication issues with very little fuss.</p><p>If your authentication system is more complicated, the other option is nested contexts &#8212; again, this is in Shoulda, but can be adapted to RSpec or the Context gem:</p><pre style="white-space: pre !important;">
context "GET edit" do

  setup do
    # generic edit setup here
  end

  context "with an anonymous user" do
    setup do
      logout
      get edit
    end

    should "not allow access" do
      assert_redirected_to root_path
    end
  end

  context "with an admin user" do
    setup do
      login_as_admin
      get edit
    end

    should "allow access" do
      assert_response :success
    end
  end

end
</pre><p>The upside of this compared with what you are probably doing is that it consolidate the setup between the different options. The downside is that it can be kind of verbose and hard to follow. But it&#8217;s still potentially easier than writing three separate tests for each action.</p><h3>Question two: I&#8217;m creating too many objects!</h3><blockquote><p>Okay, I actually can&#8217;t find the email that this came from, which is driving me crazy, because how do you lose an email message these days? Anyway, the gist was that this person&#8217;s tests were running very slowly because he or she was creating so many objects for each test &#8212; I distinctly remember the number 50 to 100 being tossed around. So the question was how to avoid creating so many objects?</p></blockquote><p>The bottom line is that there&#8217;s no way that you should need to create 50 to 100 objects for every TDD test, or even for any TDD test &#8212; it&#8217;s pretty rare that a unit-level test really needs that much data to work. There are a lot of potential issues here. Three that spring to mind are:</p><ul><li>You are trying to test too much code at one time. It&#8217;s possible (though still not very likely) that an integration test might need that many objects, but testing a single method almost never does</li><li>You&#8217;ve transitioned from fixtures to factories, but you are still writing your tests as though you were using fixtures.</li><li>You have perhaps an unnecessary fixation on providing &#8220;realistic&#8221; data in unit tests.</li></ul><p>There&#8217;s some overlap here. One cause of this is problem is that in fixtureland, there&#8217;s very little marginal cost to creating new objects, so the tendency is to create fairly large aggregations of objects that cover all possibilities and have that be the universe for all the tests. When you transition to factories, though, keeping that big data blob around is not necessary. Since you can and should be custom-creating the data for each unit test, most model methods only need one model with specific attributes in order to specify the logic. Sometimes that model will need associated objects, but the factory tool can be set to create those automatically.</p><p>A special find method or named scope can be tested with as little as two objects &#8212; one to be found and one to be skipped. Sort logic similarly can be tested with two or maybe three objects. If the logic is really complicated, it&#8217;s better to do multiple tests with small amounts of data than one test with the whole shebang. The idea of a &#8220;unit&#8221; test is to verify one small piece of logic with as little data as possible, not to check program behavior under realistic data load &#8212; that&#8217;s what integration tests, performance tests, and actual user acceptance is for.</p><p>I feel like I&#8217;m answering the question &#8220;How do I create fewer objects in my tests?&#8221; with &#8220;By creating fewer objects in your tests&#8221;, I hope this answer gives you some tools for minimizing the number of objects you need to create in your tests.</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services">Custom Software Development</a>, <a href="http://www.pathf.com//services/testing-quality-assurance/">Testing and Quality Assurance</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;linkname=Ask%20A%20Rails%20Tester%20Person" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F09%2Fask-a-rails-tester-person%2F&amp;title=Ask%20A%20Rails%20Tester%20Person" id="wpa2a_8">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/09/ask-a-rails-tester-person/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Bridging the Gap Between Rails Developers and HTML Designers</title><link>http://pathfindersoftware.com/2009/08/bridging-the-gap-between-rails-developers-and-html-designers/</link> <comments>http://pathfindersoftware.com/2009/08/bridging-the-gap-between-rails-developers-and-html-designers/#comments</comments> <pubDate>Fri, 28 Aug 2009 14:56:23 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Software Development]]></category> <category><![CDATA[User Experience Design]]></category> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[uxd]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3817</guid> <description><![CDATA[]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"><img src="http://pathfindersoftware.com/wp-content/uploads/5E22427E-BAAE-41A1-B7A8-B1FF4D55753E1.jpg" alt="5E22427E-BAAE-41A1-B7A8-B1FF4D55753E.jpg alt="Mind The Gap" border="0" width="" height="" class="right"/><br clear="all"/></div><p>To make a cheap joke and paraphrase a common quote, web developers and web designers are two groups separated by common languages. In our case, the languages are HTML and CSS, which are the output of both the web design process and the web development process. Developers and designers produce their HTML/CSS in different ways and with different goals. Here are some ideas for bridging the gap so that the developers and designers on your team can work together smoothly.</p><p>Designers and developers obviously have different goals for their HTML &#8212; developers have issues of reducing duplication, organization, and performance that are largely not the designer&#8217;s concerns. The designer is primarily concerned with how the HTML looks and behaves to the user.</p><p><span id="more-3817"></span>By the way, I&#8217;m absolutely not trying to make this some kind of left brain/right brain thing. It&#8217;s more of a software needs vs. domain expertise thing. Once upon a time, I was writing scripts that outputted router configuration files, and I had exactly the same issues with the router domain experts &#8212; my software engineering desire to structure the code without duplication conflicted with the way the router experts liked to structure their hand-written configuration instructions.</p><p>Our teams have had success with getting everybody on the team using common tools as much as possible. This means putting designs and code in the same code repository, and it means the development team supports the designers in creating a set up to run the current development version of the app locally. (By the way, <a href="http://www.viget.com/inspire/git-a-designers-perspective/">this article by Mindy Wagner</a> might be helpful if you are trying to convert everybody to Git.)</p><p>From the developer perspective, if you are working with HTML provided by designers, it&#8217;s important to keep the view layer of your code accessible to the HTML providers. Exactly what this means is subject to negotiation. Left to my own devices, I&#8217;d be putting all kinds of HTML generation in Ruby via helpers or something more esoteric. That didn&#8217;t work out well when the designer needed to go mucking about in metaprogrammed Ruby code to start changing CSS classes. We do better with putting pure logical stuff in helpers and using partials to split view logic. I&#8217;m pretty sure that if I were to suggest Haml for a project, the designers would veto it &#8212; Haml barely meshes with the way I think of HTML, the designers I&#8217;ve shown it to have basically recoiled in horror.</p><p>That said, everybody likes <a href="http//lesscss.org">Less CSS</a>, which seems to augment CSS in ways that seem very intuitive to CSS designers, and which are very satisfying to coders. It does all the things that you would expect CSS to do if it was a real language, but vanilla CSS works just fine. It really caught on quickly here.</p><p>From the designer perspective, get everything out of photoshop and into HTML/CSS as early as possible. It&#8217;s just too easy to put stuff into a photoshop image that represents hours of development work, leaving the developers in the position of trying to determine which parts of the impossible image are vital, and which are just chrome. Doing the wireframes in HTML/CSS keeps the design honest.</p><p>We use <a href="http://www.pathf.com/blogs/2008/08/integrating-design-drafts-into-your-rails-app/">this little hack</a> to integrate wireframes into the development app, which is really nice for developers when working with in-progress designs.</p><p>Ultimately what it comes down to is for everybody in the team to take some responsibility for making the team work together. The developers need to make the code base accessible to designers and to be alert to basic design issues and flexible in adapting wireframes into the site. Designers need to help place their deliverables in a format that keeps the developer from having to guess how things are supposed to work &#8212; nobody wants that.</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services/user-experience-design/">User Experience Design</a>, <a href="http://www.pathf.com/services/technology-expertise/ajax-and-rich-internet-applications/">Ajax Rich Internet Applications</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;linkname=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Fbridging-the-gap-between-rails-developers-and-html-designers%2F&amp;title=Bridging%20the%20Gap%20Between%20Rails%20Developers%20and%20HTML%20Designers" id="wpa2a_10">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/08/bridging-the-gap-between-rails-developers-and-html-designers/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Rails Testing First Look: Blue Ridge</title><link>http://pathfindersoftware.com/2009/08/rails-testing-first-look-blue-ridge/</link> <comments>http://pathfindersoftware.com/2009/08/rails-testing-first-look-blue-ridge/#comments</comments> <pubDate>Fri, 21 Aug 2009 16:26:46 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[rails testing]]></category> <category><![CDATA[tdd]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3591</guid> <description><![CDATA[Blue Ridge Mountains by eleda 1 So, I tried Blue Ridge for the first time yesterday and I thought I&#8217;d write down some quick impressions. Hence, Rails Testing First Look. Disclaimer: We came into this tool so cold our toes froze. We fumbled, we made mistakes, we probably missed really great ways of doing things. ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"> <a href="http://pathfindersoftware.com/wp-content/uploads/1713780788_6ef00e9f1d_m.jpg"><img src="http://pathfindersoftware.com/wp-content/uploads/1713780788_6ef00e9f1d_m.jpg" alt="" border="0" width="" height="" class="right"/></a><br clear="all"/><span class="right" style="font-size: smaller"><a href="http://pathfindersoftware.com/wp-content/uploads/1713780788_6ef00e9f1d_m.jpg">Blue Ridge Mountains by eleda 1</a></span></div><p>So, I tried <a href="http://github.com/relevance/blue-ridge/tree/master">Blue Ridge</a> for the first time yesterday and I thought I&#8217;d write down some quick impressions. Hence, Rails Testing First Look.</p><p>Disclaimer: We came into this tool so cold our toes froze. We fumbled, we made mistakes, we probably missed really great ways of doing things. I look forward to being enlightened.</p><p>Let&#8217;s do this question-and-answer style: <span id="more-3591"></span><strong>What is Blue Ridge?</strong> Glad you asked. It&#8217;s an framework for testing JavaScript from inside a Rails application. For the most part, it&#8217;s a cohesive bundle of existing JavaScript test tools, such as <a href="http://github.com/nkallen/screw-unit/tree/master">Screw.Unit</a>. The value-add of Blue Ridge is an easy installation and nice integration with Rails testing tasks.</p><p><strong>Why did we start using it?</strong> We had some simple JavaScript stuff to do, and thought it would be a good time to start out with what looks like a solid way to test.</p><p><strong>Is it easy to install?</strong> Very. Install as a plugin <code>./script/plugin install git://github.com/relevance/blue-ridge.git</code>, then generate the Blue Ridge files with <code>script/generate blue_ridge</code>.</p><p>Blue Ridge will install a <code>test/javascript</code> directory. In that directory will be a sample test file <code>application_spec.js</code>, a helper file, and a fixture directory. Each javascript test file is assumed to have a matching HTML file in the fixture directory which is loaded to provide a sample DOM for test purposes.</p><p><strong>Does it make any other assumptions?</strong> BlueRidge assumes you are using jQuery as your library. It&#8217;s theoretically possible to use Prototype, but we ran into continual difficulties (possibly because we were doing it wrong), and actually wound up switching to jQuery for the project, easy enough to do since we just started.</p><p><strong>How do you write tests?</strong> Tests are written using Screw.Unit syntax, and if you are familiar with RSpec, the syntax will look pretty similar. Here&#8217;s the final draft of the first couple of tests we wrote, for a search box with default text that goes away when focused. The TextMate Screw.Unit bundle was very helpful, but other than that, the tests are pretty straightforward.</p><pre style="white-space: pre !important;">
Screw.Unit(function() {
  describe("With my search box and default", function() {
    it("should switch the default", function() {
      search_focus($('#unified_search'));
      expect($('#unified_search').attr('value')).to(equal, '');
    });

    it("should switch a non-default", function() {
      $('#unified_search').addClass('search_entry');
      search_blur($('#unified_search'));
      expect($('#unified_search').attr('value')).to(equal, default_text);
    });

    it("should not switch if there is a value", function() {
      $('#unified_search').attr('value', 'Fred');
      search_blur($('#unified_search'));
      expect($('#unified_search').attr('value')).to(equal, 'Fred');
    });

  });
});
</pre><p><strong>How do you run tests?</strong> Two ways. Blue Ridge provides a <code>test:javascripts</code> Rake task that will run all your Javascript tests in the terminal, this is also suitable for including in your continuous integration build, for example. A very nice feature from Screw.Unit is the ability to run tests in browser by simply opening the fixture HTML file in the browser of your choice. As long as your choice isn&#8217;t Safari, which isn&#8217;t supported at the moment. The ability to run the tests in console and in browser is very useful.</p><p><strong>Any gotchas?</strong> Oy. We had what appeared to be some minor differences between the Rhino implementation that powers the console tests and the browser tests, leading to tests passing in the browser and failing in the console. We weren&#8217;t quite able to do what amounted to integration tests &#8212; we tried to trigger the JavaScript events so that the jQuery functions tested above were triggered, but couldn&#8217;t get that to work. You have to be careful that the fixture HTML file actually has all the DOM elements you need, and remember that fixing the actual view doesn&#8217;t change the fixture. Screw.Unit&#8217;s error messages on failure aren&#8217;t as helpful as they might be, there also seemed to be a thing where the fixture DOM wasn&#8217;t actually being reset between tests, but I&#8217;m not 100% sure about that, we wound up working around it.</p><p><strong>And in the end?</strong> That said, I did feel better about my JavaScript after having tested it. Fixing the JavaScript code, it was nice to see that other functions hadn&#8217;t broken. There&#8217;s a lot to like here, and I&#8217;m hoping to get past my initial problems to a tool that will improve the JavaScript parts of my code.</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services">Custom Software Development</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;linkname=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Frails-testing-first-look-blue-ridge%2F&amp;title=Rails%20Testing%20First%20Look%3A%20Blue%20Ridge" id="wpa2a_12">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/08/rails-testing-first-look-blue-ridge/feed/</wfw:commentRss> <slash:comments>6</slash:comments> </item> <item><title>Functional Testing Annoyances, Wapcaplet, And You</title><link>http://pathfindersoftware.com/2009/08/functional-testing-annoyances-wapcaplet-and-you/</link> <comments>http://pathfindersoftware.com/2009/08/functional-testing-annoyances-wapcaplet-and-you/#comments</comments> <pubDate>Fri, 07 Aug 2009 21:45:10 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[rails testing]]></category> <category><![CDATA[tdd]]></category> <category><![CDATA[Test Driven Development]]></category> <category><![CDATA[Testing]]></category> <category><![CDATA[wapcaplet]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3436</guid> <description><![CDATA[Here&#8217;s a minor thing that bugs me all the time. I&#8217;m writing a functional test: should "do something functional" get :search, rder_id => @order.id, :user_id => @user.id # and so on end The get call in that test simulates a browser request. Intuitively, you would (well, I would) expect this request to be identical to ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><p>Here&#8217;s a minor thing that bugs me all the time.</p><p>I&#8217;m writing a functional test:</p><pre style="white-space: pre !important;">
should "do something functional"
  get :search, <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> rder_id => @order.id, :user_id => @user.id
  # and so on
end
</pre><p>The <code>get</code> call in that test simulates a browser request. Intuitively, you would (well, I would) expect this request to be identical to a request coming from the actual view, via a helper like <code>link_to("search", :action => :search, <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> rder_id => @order.id, :user_id => @user.id)</code>. At least, you&#8217;d expect that parameters hash in the controller to be the same between the</p><p>Makes sense, right? The testing call should set up the same environment as the actual call being tested.<span id="more-3436"></span>Well, if you&#8217;ve gotten this far, read the title that said &#8220;Annoyances&#8221;, or have ever read any blogs, you know that isn&#8217;t how Rails works. Specifically, the actual browser call has all its arguments converted to strings as part of going through the HTTP wire, but Rails, oddly, does not similarly convert the arguments in the test.</p><p>Most of the time it makes no difference &#8212; if you pass the string or the integer directly to an ActiveRecord find method, everything works swimmingly. If you do a direct equality check on the parameter, though, you can get code that passes tests, but fails in the browser because the string value is no longer equal to the integer value.</p><p>For some time, I&#8217;ve had this on my list of annoyances that I don&#8217;t have the energy to fix, right next to the slightly misaligned light over my desk, and the fact that they bought the wrong kind of Kleenex at the office that one time.</p><p>This week, <a href="http://pivotallabs.com/users/amilligan/blog/articles/951-wapcaplet">Adam Milligan at Pivotal Labs</a> tried to do something about it. Specifically, he created a Rails patch, then a plugin, that can raise a warning or an error when a non-string argument is passed to an HTTP method in a functional test.</p><p>Adam chose to fail or warn rather than silently convert the parameter because he feels that some arguments that might get placed in an HTTP method don&#8217;t have clear correct conversions (his example is <code>false</code>), and he&#8217;s trying to avoid setting up a whole different kind of difficult-to-diagnose test to app misalignments.</p><p>As much as I love that Adam did this, I kind of think that it&#8217;s more consistent with Rails design to silently convert, to make the browser call and the test consistent. Still, having the code warn or fail (your choice) is a pretty good way to get into good habits and avoid having to track down weird test errors. So, thanks, Adam.</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services">Custom Software Development</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;linkname=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F08%2Ffunctional-testing-annoyances-wapcaplet-and-you%2F&amp;title=Functional%20Testing%20Annoyances%2C%20Wapcaplet%2C%20And%20You" id="wpa2a_14">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/08/functional-testing-annoyances-wapcaplet-and-you/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Help, My Test Is Failing!</title><link>http://pathfindersoftware.com/2009/07/help-my-test-i-failing/</link> <comments>http://pathfindersoftware.com/2009/07/help-my-test-i-failing/#comments</comments> <pubDate>Fri, 31 Jul 2009 17:11:58 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[tdd]]></category> <category><![CDATA[Test]]></category> <category><![CDATA[Test Driven Development]]></category> <category><![CDATA[Testing]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3399</guid> <description><![CDATA[Frustration, the game, photo by unlovalblesteve Dot, dot, dot, dot, dot &#8212; tests are passing, looks like it&#8217;s time for lunch &#8212; dot, dot, dot, dot, F. F? F? But the code works. I know it does. I think it does. Why is my test failing? One of the most frustrating times as a TDD ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"><a href="http://www.flickr.com/photos/unloveable/2398611730/"><img src="http://pathfindersoftware.com/wp-content/uploads/2398611730_4d027256e0_m_d.jpg" alt="Frustration, the game" border="0" width="" height="" class="right"/></a><br clear="all"/><span class="right" style="font-size: smaller"><a href="http://www.flickr.com/photos/unloveable/2398611730/">Frustration, the game, photo by unlovalblesteve</a></span></div><p>Dot, dot, dot, dot, dot &#8212; tests are passing, looks like it&#8217;s time for lunch &#8212; dot, dot, dot, dot, F. F? F? But the code works. I know it does. I think it does. Why is my test failing?</p><p>One of the most frustrating times as a TDD developer is that moment when a test is failing and you don&#8217;t know why, as opposed to the more normal case where the test fails as expected. Here&#8217;s a grab bag of tips, tricks, hints, and thoughts to get us all through that difficult time.</p><div style="border: thin solid blue; padding: 5px"> <strong>Self-promotion alert:</strong> More details about Rails testing can be found at <a href="http://www.railsrx.com">Rails Test Prescriptions</a>, there&#8217;s a free getting started tutorial which contains an extensive section on Cucumber, and a <a href="http://www.lulu.com/content/e-book/rails_test_prescriptions/6418439">nearly 300 pages and counting full book for $9</a>. Thanks. Also, follow <a href="http://www.twitter.com/railsrx">@railsrx</a> on Twitter for testing tips and updates.</div><p><span id="more-3399"></span><br /><h3>Something Must Have Changed</h3><p>This may be the most obvious piece of advice in the history of ever, but I find it&#8217;s worth repeating, mantra-like, when confronted with a bad bug:</p><p>When a formerly-passing test fails, it means something changed.</p><p>It may be in the code, or the system, or the test. But it&#8217;s probably not sunspots, and it&#8217;s probably not evil spirits possessing your MacBook. (Unless you are either <a href="http://www.amazon.com/gp/product/0441016685?ie=UTF8&#038;tag=10prinhell-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0441016685">living in a Charles Stross novel</a> or writing Perl, but I digress&#8230;)</p><p>Looking through recent changes can help figure out what the cause of the failure is. Git&#8217;s bisect tool can do this automatically, or you can just look through recent changes in your source control viewer of choice. If the test was passing at one time, there&#8217;s a good chance the answer is in there somewhere.</p><p>This is a great argument in favor of committing to your source control very, very frequently (especially when you are using git and can do local commits), so that your changes are very granular.</p><h3>Isolate</h3><p>When looking at a small number of failing tests, it&#8217;s helpful to be able to run just those tests. Autotest is outstanding for this, since it will run the failing tests over and over until they pass. This is especially helpful if you have a number of failing tests that are not in the same test class.</p><p><a href="http://gist.github.com/101130">This little code and terminal snippet</a> is very helpful for quickly running one class at a time, which is almost like isolating a failing test, or at least close enough to be useful. Depending on your IDE and test framework of choice, you may also be able to run individual tests from the IDE.</p><p>Isolating tests makes the tests run faster when you are focused on just a few tests, and also makes any diagnostics you insert easier to interpret.</p><p>Two tips that I&#8217;ve stolen from listening to and reading Kent Beck:</p><ul><li>Back out your entire most recent change since your last passing test and start over. This works best if you work in very small increments, but it gets you out of the &#8220;I know I typed something wrong but I just can&#8217;t see it&#8221; nightmare</li><li>Replace all the expressions in the method under test with literals &#8212; if that passes, then put the expressions back one by one until you find the culprit.</li></ul><h3>Diagnose</h3><p>I have to say, I&#8217;m not a big fan of using stop-and-step debuggers. I&#8217;ve used them when I&#8217;ve been in an IDE, I&#8217;ve never really used the Rails command line debugger, but mostly I&#8217;ve found that not to be a great experience.</p><p>Normally, to diagnose what&#8217;s going on in a test, I usually either add additional assertions in the test or have the code print information to the console. If I diagnose via assertions, generally I&#8217;m testing the values of variables in more detail.</p><p>For some reason, I see a lot of people using Ruby&#8217;s <code>puts</code> method to write to the console &#8212; I recommend <code>p</code>, which calls <code>inspect</code> on the object before printing, and generally results in more informative output. As a matter of course, I put <code>require pp</code>, which allows me to use <code>pp</code> to get pretty-printed output, which is nice for nested data structures. Also, <code>y</code> gives a YAML representation of the output &#8212; very readable for ActiveRecord objects.</p><pre style="white-space: pre !important;">
>> x = {1 => ['a', 'b'], 2 => 'c'}
>> puts x
1ab2c

>> p x
{1=>["a", "b"], 2=>"c"}

>> pp x
{1=>["a", "b"], 2=>"c"}

>> y x
---
1:
- a
- b
2: c
</pre><p>Especially if I have autotest running just the one test, I&#8217;ve been known to bury print statements all over the place &#8212; controllers, Rails itself (often educational). Just remember to take them out when you are done.</p><h3>Clear Your Head</h3><p>Take a walk. Force your pair to solve the problem. Get a cup of coffee (actually, I hate coffee, get a Diet Coke). Take a nap. All those silly clear your head things really do work sometimes.</p><h3>Band Aids</h3><p>It&#8217;s tempting sometimes to comment out the offending test and then your suite passes and all is well with the world again. That&#8217;s generally a bad idea (although sometimes a major refactoring can genuinely make tests obsolete.</p><p>Hope this helps. What do you do to fix stubborn tests?</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services">Custom Software Development</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;linkname=Help%2C%20My%20Test%20Is%20Failing%21" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fhelp-my-test-i-failing%2F&amp;title=Help%2C%20My%20Test%20Is%20Failing%21" id="wpa2a_16">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/07/help-my-test-i-failing/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Real Testing Example, Part Two</title><link>http://pathfindersoftware.com/2009/07/real-testing-example-part-two/</link> <comments>http://pathfindersoftware.com/2009/07/real-testing-example-part-two/#comments</comments> <pubDate>Fri, 24 Jul 2009 16:45:19 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[rails development]]></category> <category><![CDATA[rails testing]]></category> <category><![CDATA[tdd]]></category> <category><![CDATA[Test Driven Development]]></category> <category><![CDATA[Testing]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3356</guid> <description><![CDATA[Spam Wall, by freezelight What with upward of two people saying nice things about last week&#8217;s post, I&#8217;ve decided to keep going with part two of a look at some real testing code. Most code-heavy tutorials show the code but not the tests &#8212; I&#8217;m doing the opposite here, and showing the tests, but not ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"><a href="http://www.flickr.com/photos/63056612@N00/155554663/"><img src="http://pathfindersoftware.com/wp-content/uploads/155554663_89beb0ac63_m1.jpg" alt="" border="0" width="" height="" class="right"/></a><br clear="all"/><span class="right" style="font-size: smaller"><a href="http://www.flickr.com/photos/63056612@N00/155554663/">Spam Wall, by freezelight</a></span></div><p>What with upward of two people saying nice things about <a href="http://www.pathf.com/blogs/2009/07/a-real-testing-example/">last week&#8217;s post</a>, I&#8217;ve decided to keep going with part two of a look at some real testing code.</p><p>Most code-heavy tutorials show the code but not the tests &#8212; I&#8217;m doing the opposite here, and showing the tests, but not much of the code. Also, although I&#8217;m presenting these tests in chunks, you should realize that there was a lot of back-and-forth from Cucumber to tests to code and some backtracking, most of which I&#8217;ll spare you from having to wade through.</p><p>At the end of last week, I had run through the tests for spam-prevention code which worked by limiting the rate at which a user could send messages to other users of a particular social networking site. Cucumber was involved, and I think I went off on a tangent about writing lots of tests.</p><div style="border: thin solid blue; padding: 5px"> <strong>Self-promotion alert:</strong> More details about Rails testing can be found at <a href="http://www.railsrx.com">Rails Test Prescriptions</a>, there&#8217;s a free getting started tutorial which contains an extensive section on Cucumber, and a <a href="http://www.lulu.com/content/e-book/rails_test_prescriptions/6418439">nearly 300 pages and counting full book for $9</a>. Thanks. Also, follow <a href="http://www.twitter.com/railsrx">@railsrx</a> on Twitter for testing tips and updates.</div><p><span id="more-3356"></span><p>Once the basic rate limiting code was in place, the client and I came up with a couple of special cases. The application allows a user to explicitly reply to a specific message sent to them. We agreed that these replies should not count towards the rate limit on the grounds that a reply was, pretty much by definition, not spam. Similarly, we decided that any message between users with a friend relationship on the site also doesn&#8217;t count toward the rate limit. More subtly, we decided that if a user was inadvertently blocked, then reinstated by an administrator, that their rate count should drop back to zero, so the user doesn&#8217;t just get immediately re-blocked.</p><p>The Cucumber tests:</p><pre><code>Scenario: Reply behavior
  Given I am a user who is not a new member
  When I send 4 messages in a day
  And I reply to a message
  And I send 1 message in a day
  Then 6 messages are sent
  And I am not blocked from sending further messages
  And the administrator does not get an email

Scenario: Friend Behavior
  Given I am a user who is not a new member
  When I send 4 messages in a day
  And I send a message to a friend
  And I send 1 message in a day
  Then 6 messages are sent
  And I am not blocked from sending further messages
  And the administrator does not get an email

Scenario: Unblocked behavior
  Given I am a user who is not a new member
  When I send 6 messages in a day
  Then I am blocked from sending further messages
  When I am unblocked by an administrator
  And I send 5 messages in a day
  Then 5 messages are sent
  And I am not blocked from sending further messages
  And the administrator does not get an email
</code></pre><p>These tests are quite similar in structure to the tests I started with last week. I might start thinking about combining some of the steps, but in Cucumber, I bias in favor of having the actual test be as clear and readable as I can get it. I don&#8217;t think these scenarios are so complicated that they require simplification.</p><p>Most of these steps have already been defined, here are a couple that aren&#8217;t. The first two steps are slight variants on the message sending step definition:</p><pre><code>When /^I reply to a message$/ do
  original_message = Factory.create(:message, :sender =&gt; @recipient,
      :recipient =&gt; @sender)
  message = Factory.attributes_for(:message, :sender =&gt; @user)
  message[:in_reply_to] = original_message.id
  visit(messages_path,
      :post, {:recipient =&gt; @recipient.id, :message =&gt; message})
end

When /^I send a message to a friend$/ do
  @user.become_friends_with(@recipient)
  visit(messages_path,
      :post,
      {:recipient =&gt; @recipient.id,
          :message =&gt; Factory.attributes_for(:message, :sender =&gt; @user)})
end
</code></pre><p>One thing that you may have noticed about these step definitions. Despite all my jumping up and down about Cucumber being a black-box system, both of these steps bypass the UI to complete the setup. The first step creates a factory message and the second step creates a friend relationship. In both cases, the actual message send being tested passes through the UI.</p><p>There are basically two reasons for bypassing the UI in the setup. One is, of course, that it&#8217;s the setup and not the action being tested, the second is that being a purist all the time is a pain, and it&#8217;s much easier to get the background info out of the way directly.</p><p>The final missing step, however, is completely through the UI &#8212; the step logs the user out, logs in as an administrator, navigates to the page, unblocks the user, and then logs the administrator out and the original user back in.</p><pre><code>When /^I am unblocked by an administrator$/ do
  @blocked = @user
  visit "/logout"
  Given "I am a logged in administrator"
  visit path_to("the admin messaging page")
  click_link("Unblock")
  @user = @blocked
  visit "/logout"
  Given "I am logged in"
  reset_mailer
end
</code></pre><p>A little on the Rube Goldberg side, perhaps, but it works.</p><p>With the Cucumber step definitions in place, the question becomes what tests and code need to be written to make these pass. For the first two scenarios that adjust the definition of what makes a message count toward the rate limit, the code change will be in the User and Message models. Since it&#8217;s a thin controller that defers to the models for data, there shouldn&#8217;t be any code change in the controller at all.</p><p>Now, that&#8217;s a much easier determination to make in hindsight than up front, and as it happens I did write controller tests for both of those scenarios, largely because they were so similar to the controller context chunks in last week&#8217;s post that it only took about five minutes to set them both up.</p><p>The real test action was in the user and message models. I added a database column for messages called <code>counts_toward_spam</code>. The idea is that this would be true for most messages, but false for replies, friend messages, or messages otherwise cleared by the admins. This requires some unit tests in the message class to support it. Again, I&#8217;m using Matchy here, and testing the boolean <code>spam_message?</code>, which is basically an alias for <code>counts_toward_spam</code> that I added for readability. In retrospect, adding the method was probably unnecessary.</p><pre><code>context "spam count" do

  setup do
    @sender = Factory.create(:user)
    @recipient = Factory.create(:user)
  end

  should "give an ordinary message a spam count" do
    @message = Message.new_from_params(
        {:subject =&gt; "fred", :body =&gt; "body"}, @sender, @recipient)
    @message.sender_id.should == @sender.id
    @message.recipient_id.should == @recipient.id
    @message.should be_spam_message
  end

  should "not give an ordinary message a spam count if it is a reply" do
    @message = @message = Message.new_from_params(
        {:subject =&gt; "fred", :body =&gt; "body", :in_reply_to =&gt; 11},
        @sender, @recipient)
    @message.should be_reply
    @message.should_not be_spam_message
  end

  should "not make a message between mutual friends a spam count" do
    @sender.become_friends_with(@recipient)
    @message = Message.new_from_params(
        {:subject =&gt; "fred", :body =&gt; "body"}, @sender, @recipient)
    @message.should be_friend_message
    @message.should_not be_spam_message
  end
end
</code></pre><p>The message tests simply set up the different classes of message and verify that they are classified appropriately, they are pretty straightforward.</p><p>I also added a test to user to verify that the spam count was being used appropriately &#8212; this is part of the same context as last week&#8217;s user tests. The user has already sent four messages in the setup.</p><pre><code>should "not count a reply message" do
  message = Factory.create(:message, :sender =&gt; @user,
      :created_at =&gt; 10.minutes.ago)
  message.in_reply_to = 3
  message.update_count_toward_spam
  message.save!
  @user.spam_message_count.should == 4
  @user.update_message_block_status.should be_nil
  @user.should be_able_to_send_messages
end
</code></pre><p>It&#8217;s interesting what you see when you go back over a chunk of code to explain it to other people. In this case what I notice is the <code>update_count_toward_spam</code> method, which is part of Message that unsets the <code>count_toward_spam_</code> field if the message is a reply and is automatically called as part of the controller create message code. However, strictly speaking, it should be tested in the message class rather than here (although, the message controller tests would also exercise it).</p><p>That&#8217;s the bulk of the testing for those features &#8212; at the time, I didn&#8217;t think there were any other special cases.</p><p>The part about unblocking a user requires controller tests, because the unblock action needs to be written in the controller. I&#8217;m making it part of the message controller, although I know that RESTfully speaking it probably should be a separate resource &#8212; the legacy app is such a REST tangle that it&#8217;s not worth it.</p><p>The controller test context looks like this &#8212; the setup creates a blocked user and calls the unblock method.</p><pre><code>context "GET unblock" do

  setup do
    ActionMailer::Base.deliveries.clear
    @recipient = Factory.create(:user)
    @user = Factory.create(:user,
      :message_sending_status =&gt; User::BLOCKED_STATUS)
    @user.should_not be_able_to_send_messages
    admin!
    get :unblock, :user_id =&gt; @user.id.to_s
    @user.reload
  end

  expect { @user.should be_able_to_send_messages }
  expect { assert_redirected_to :action =&gt; :admin }

  should "send email to unblocked user" do
    assert_sent_email do |email|
      email.to.first == @user.email &amp;&amp;
      email.from.first == "do_not_reply@singlestravelintl.com"
    end
  end
end
</code></pre><p>The assertion tests verify that the user can send messages again, and that the user gets an email from the system to that effect. The associated user model test validates that the user&#8217;s spam message count is reset back to zero, which was the specific point of this round of tests (in the actual application, there were other Cucumber scenarios supporting basic administrative behavior).</p><pre><code>context "unblocking a user" do

  setup do
    Message.new_from_params(Factory.attributes_for(:message,
        :created_at =&gt; 10.minutes.ago), @user,
         Factory.create(:user)).save!
    @user.update_message_block_status.should == "blocked"
    @user.should_not be_able_to_send_messages
    @user.unblock_message_status
  end

  expect { @user.should be_able_to_send_messages }
  expect { @user.spam_message_count.should == 0 }

  should "reblock" do
    @user.block_message_status
    @user.should_not be_able_to_send_messages
  end

end
</code></pre><p>Anyway, the code that I wrote along side these tests to make them pass seemed to work fine, and we deployed to production.</p><p>Shortly thereafter, we got a bug report. I haven&#8217;t stressed it here, but new users were limited to a single message in their earliest time on the system. We heard from a new user who sent a message, received a reply, and then replied back to that message, only to find that he was blocked, even though the reply message shouldn&#8217;t count toward the rate limit.</p><p>Interesting. It&#8217;s one of those bugs that almost turns philosophical &#8212; exactly when in this process does a user become blocked? In the code that I originally wrote, a user was blocked as soon as he or she hit the rate limit, and before sending another message. In fact, the code should wait until the user tries to send that next message to consider that user blocked, because a user who is at the rate limit border should still be able to send replies and friend messages without being blocked.</p><p>So, bug. Since the bug involves multiple interactions, I started in Cucumber:</p><pre><code>Scenario: New User Behavior on reply
  Given I am a new user
  When I send 1 message in a day
  And I reply to a message
  Then 2 messages are sent
  And I am not blocked from sending further messages
</code></pre><p>All those step definitions exist, but the last two steps fail without further work. I wasn&#8217;t initially sure whether the code change for this would wind up in the controller or not &#8212; as it happened, it turned out to be a minor controller change and minor model change but it took me a few tries to get it right.</p><p>Since I already had a decent controller harness for similar scenarios, it took almost no time to adapt to the new condition:</p><pre><code>context "with a borderline new user and a reply" do

  setup do
    reply = Factory.create(:message, :recipient =&gt; @user,
        :sender =&gt; Factory.create(:user))
    flexmock(User).should_receive(:new_user?).and_return(true)
    flexmock(Message).should_receive(
        :toward_spam_in_last_24_hours).and_return(1)
    post :create, :recipient =&gt; @recipient.id,
        :message =&gt; Factory.attributes_for(:message, :sender =&gt; @user,
            :in_reply_to =&gt; reply.id.to_s)
    @user.reload
  end

  expect { @user.messages_sent.size.should == 1 }
  expect { @user.spam_message_count.should == 1 }
  expect { assigns(:message).should_not be_new_record }
  expect { @user.should_not be_message_sending_blocked }
  expect { assert_no_email_to_administrator }
end
</code></pre><p>I have to say, in retrospect, that I&#8217;m not 100% sold on the use of the mock package here &#8212; it&#8217;s a clear user of mocking to limit the test&#8217;s exposure to the model layer, but I&#8217;m not convinced it makes the test more clear or readable. This setup and tests is very similar to the other bundles of controller test and makes most of the same assertions.</p><p>The main change was in the user method, triggered by the following tests &#8212; one of which tests the positive sequence, one the negative &#8212; and yes, the setups probably should have been combined.</p><pre><code>should "move a new user to blocked mode after first message" do
  Timecop.freeze(Date.today)
  new_user = Factory.create(:user, :created_at =&gt; 1.day.ago)
  m = Message.new_from_params(Factory.attributes_for(:message),
      new_user, Factory.create(:user))
  m.created_at = 2.hours.ago
  m.save!
  assert_equal(2.hours.ago, new_user.last_message_sent_time)
  assert_equal("blocked", new_user.update_message_block_status(true))
  assert new_user.message_sending_blocked?
end

should "allow a new user a second message that is a reply" do
  Timecop.freeze(Date.today)
  new_user = Factory.create(:user, :created_at =&gt; 1.day.ago)
  m = Message.new_from_params(Factory.attributes_for(:message),
      new_user, Factory.create(:user))
  m.created_at = 2.hours.ago
  m.save!
  assert_equal(2.hours.ago, new_user.last_message_sent_time)
  assert_nil new_user.update_message_block_status(false)
  assert !new_user.message_sending_blocked?
end
</code></pre><p>The key here &#8212; which would be easier to see if the setups were combined &#8212; is the next to last line where <code>update_message_block_status</code> is called. That&#8217;s the method called by the controller when a new message is sent, and the <code>false</code> argument means that the new message does not count toward the rate limit. So in the second test, the user can still send messages, while in the first test, the user does move to a blocked status.</p><p>Having the existing suite of tests around the controller and model behaviors was a big relief when fixing this bug &#8212; it&#8217;d be easy to make a fix for this issue that affected one of the other scenarios (I know because I made fixes that broke a lot of tests&#8230;). This was a case where the tests clearly made me more confident in the fix that I made.</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services">Full Life Cycle Software Development</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;linkname=Real%20Testing%20Example%2C%20Part%20Two" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Freal-testing-example-part-two%2F&amp;title=Real%20Testing%20Example%2C%20Part%20Two" id="wpa2a_18">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/07/real-testing-example-part-two/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>A Real Testing Example</title><link>http://pathfindersoftware.com/2009/07/a-real-testing-example/</link> <comments>http://pathfindersoftware.com/2009/07/a-real-testing-example/#comments</comments> <pubDate>Fri, 17 Jul 2009 21:36:04 +0000</pubDate> <dc:creator>Noel Rappin</dc:creator> <category><![CDATA[Ruby on Rails]]></category> <category><![CDATA[Software Development]]></category> <category><![CDATA[rails testing]]></category> <category><![CDATA[Test Driven Development]]></category> <category><![CDATA[Testing]]></category><guid isPermaLink="false">http://www.pathf.com/blogs/?p=3265</guid> <description><![CDATA[Spam Wall, by freezelight As sort-of promised in last week&#8217;s post, I&#8217;m going to work through a real-world test example, with an eye toward explaining how and why I tested the way I did. Hopefully, I&#8217;ll be able to do this at blog-post length. If not, well, there&#8217;s always next week. This site, which was ...]]></description> <content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"> <a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F"><br /> <img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br /> </a></div><div class="right"><a href="http://www.flickr.com/photos/63056612@N00/155554663/"><img src="http://pathfindersoftware.com/wp-content/uploads/155554663_89beb0ac63_m.jpg" alt="" border="0" width="" height="" class="right"/></a><br clear="all"/><span class="right" style="font-size: smaller"><a href="http://www.flickr.com/photos/63056612@N00/155554663/">Spam Wall, by freezelight</a></span></div><p>As sort-of promised in <a href="http://www.pathf.com/blogs/2009/07/to-mock-or-not-to-mock/">last week&#8217;s post</a>, I&#8217;m going to work through a real-world test example, with an eye toward explaining how and why I tested the way I did. Hopefully, I&#8217;ll be able to do this at blog-post length. If not, well, there&#8217;s always next week.</p><p>This site, which was a legacy rescue, allows users to send messages to each other within the site without having to give away their other contact information. The problem is that nefarious spammer types were creating logins and immediately sending messages to large numbers of the user population, irritating them. After some deliberation, the client decided on a rate-limiting strategy, where a member could only send a certain number of messages in a day, and a new member could send even fewer messages a day. Messages above that point would require administrative action to unblock the user&#8217;s privileges.</p><div style="border: thin solid blue; padding: 5px"> <strong>Self-promotion alert:</strong> More details about Rails testing can be found at <a href="http://www.railsrx.com">Rails Test Prescriptions</a>, there&#8217;s a free getting started tutorial which contains an extensive section on Cucumber, and a <a href="http://www.lulu.com/content/e-book/rails_test_prescriptions/6418439">nearly 300 pages and counting full book for $9</a>. Thanks. Also, follow <a href="http://www.twitter.com/railsrx">@railsrx</a> on Twitter for testing tips and updates.</div><p><span id="more-3265"></span>I started with Cucumber tests &#8212; the first two are representative of the initial batch. Everything you see here is actual code, only slightly tweaked to anonymize details of the site. But I&#8217;m trying not to present simplified &#8220;example&#8221; code. That said, this is the final state of the code, there were some intermediate steps and false trails that I&#8217;m sparing you from having to read about. I&#8217;m also focusing on the tests here, rather than the resulting application code.</p><pre><code>Background:
  Given the user database is cleared
  Given a group of recipients.

Scenario: Normal User behavior
  Given I am a user who is not a new member
  When I try to send 5 messages in a day
  Then 5 messages are sent
  And the administrator does not get an email

Scenario: Excessive User behavior
  Given I am a user who is not a new member
  When I try to send 6 messages in a day
  Then 5 messages are sent
  And I am blocked from sending further messages
  And the administrator gets an email
</code></pre><p>This test uses an implicit style, so a lot of the details are in the step definitions. I start by writing the step definitions one by one until I get to one that I can&#8217;t make work without writing new code. As it happens, since not sending email is the current state of the system, the entire first scenario should work without any new code. It&#8217;s very important to include that scenario, though, to ensure that the new changes don&#8217;t break the basic behavior.</p><p>Here&#8217;s what the steps look like &#8212; the user who is not a new member defers to the boilerplate RESTful Authentication step for logging in:</p><pre><code>Given /^I am a user who is not a new member$/ do
  @user = Factory.create(:user, :created_at =&gt; 5.months.ago)
    Given "I am logged in"
end

Given /^I am logged in$/ do
  visit "/login"
  fill_in("email", :with =&gt; @user.email)
  fill_in("password", :with =&gt; @user.password)
  click_button("Sign in")
end
</code></pre><p>Next up, sending messages. Since the point of Cucumber is to treat the application as a black box, the step definition uses Webrat to simulate number of posts to the create message RESTful action. (The <code>@recipient</code> is created in the background action, which I didn&#8217;t show here because it&#8217;s not very interesting.)</p><pre><code>When /^I try to send (.*) message(.*) in a day$/ do |count, plural|
  count.to_i.times do
    visit(messages_path, :post, {:recipient =&gt; @recipient.id,
             :message =&gt; Factory.attributes_for(:message,
                                :sender =&gt; @user)})
  end
end
</code></pre><p>So this step definition allows you to match &#8220;send 3 messages&#8221; and &#8220;send 1 message&#8221; by adding that little group at the end of the message, that group needs to have an associated variable in the block argument list, but it&#8217;s just ignored. Somebody with better offhand regular expression skills could easily make it so the end of the expression only matches &#8220;message&#8221; or &#8220;messages&#8221;, but I can live with it being a little overly matchy for now.</p><p>Closing out the normal case, the step definition to test emails sent uses the excellent<br /> <a href="http://github.com/bmabey/email-spec/tree/master">email_spec</a> plugin, which creates RSpec matchers and Cucumber step definitions for email testing. This one checks an <code>all_emails</code> method, filters out any methods sent to the administrator, and makes sure the right number exist.</p><pre><code>Then /^(.*) message(.*) (is|are) sent$/ do |count, plural, verb|
  emails_out = all_emails.select do |e|
    !e.to.include?("admin@admin.com")
  end
  assert_equal(count.to_i, emails_out.size)
end
</code></pre><p>The administrator email step definition uses the email_spec steps directly &#8212; and yes, I could have included those steps explicitly in the Cucumber scenario. I chose not to, on the perhaps dubious grounds that I wanted to keep explicit string literals out of the scenario. But it doesn&#8217;t make that much difference either way.</p><pre><code>Then /^the administrator (gets|does not get) an email$/ do |status|
  if status == "gets"
    Then '"admin@admin.com" should receive an email'
  else
    Then '"admin@admin.com" should not receive an email'
  end
end
</code></pre><p>Everything here passes for the normal case. So far, my main note is that all the step definitions are really simple &#8212; it&#8217;s almost impossible to misinterpret them.</p><p>At this point, I move on to the blocking case. There&#8217;s one more step definition to write. In actuality, the &#8220;then 5 messages are sent&#8221; step will fail, since the blocking isn&#8217;t in the code. So, I would normally jump to writing regular tests at that point, but since I&#8217;m here, I&#8217;ll present the last step definition.</p><p>Cucumber is a black box, therefore in order to detect if the user is blocked, we need to find some place in the application that will display it. After  their initial notification, the user doesn&#8217;t see anything confirming their block status. But the administrator does, via an admin screen that actually hasn&#8217;t been written yet. So, the cucumber tests logs the user out, logs an admin in, and checks the admin page for a row associated with the user.</p><pre><code>Then /^I am blocked from sending further messages$/ do
  @sender = @user
  visit "/logout"
  Given "I am a logged in administrator"
  @admin = @user
  visit path_to("the admin messaging page")
  assert_select("tr#?", dom_id(@sender, :blocked_row), :count =&gt; 1)
end
</code></pre><p>This step definition is a bit more complicated than the others, and it&#8217;s also taking it slightly on faith that the row with the correct DOM id will actually have the information the admin needs. (The tradeoff in view testing, as  always is faith an flexibility vs. certainty and brittleness&#8230;)</p><p>Okay, there are genuinely failing Cucumber steps, it&#8217;s time to make them pass.</p><p>A couple of things to note:</p><ul><li>After going back and forth on this some, I&#8217;ve decided that Cucumber tests don&#8217;t really replace controller tests, except in maybe the simplest cases. My rationale is that Cucumber and controller tests look at different error conditions, and that Cucumber tests are really (partially) replacing the hand-testing that I would be doing to verify that the feature is working. Cucumber is the 30,000 foot view of the application, I still feel the need to test from the ground.</li><li>This app was a legacy with effectively no tests, leading me to have to write some extra tests to cover the normal, non-blocking case. If the whole app was TDD, then that functionality would already have tests, but since I&#8217;m working on the new features, I need to go back and cover the base functionality.</li></ul><p>The basic ideas is write the tests in the class where the code is going to go. I can guess that code will need to go in the <code>MessageController</code>, the <code>Message</code> model, and the <code>User</code> model. In fact, I&#8217;m going to need a new field in the user model, a string representing message sending status. (It&#8217;s a string and not a boolean, because I know from a later requirement that there will be more than two states). I actually will wait to write the migration, though, until a test compels it.</p><p>My preference is to start with the controller tests &#8212; it&#8217;s the easiest way into the system for me.</p><p>Here&#8217;s the batch of tests I wrote to cover the normal sending a message and not getting blocked functionality. These tests will probably look weird to just about everybody since I wrote them using a combination of <a href="http://www.thoughtbot.com/projects/shoulda/">Shoulda</a>, <a href="http://github.com/giraffesoft/zebra/tree/master">Zebra</a>, and <a href="http://github.com/jeremymcanally/matchy/tree/master">Matchy</a>. Zebra gives nice one-line tests, and Matchy gives a sort-of RSpec syntax that I sometimes like.</p><pre><code>context "POST CREATE" do
  setup do
    ActionMailer::Base.deliveries.clear
    @recipient = Factory.create(:user)
    @user = login!
  end

  context "with a clean user" do
    setup do
      post :create, :recipient =&gt; @recipient.id,
          :message =&gt; Factory.attributes_for(:message,
                :sender_id =&gt; @user.id)
    end

    expect { @user.messages_sent.size.should == 1 }
    expect { @user.spam_message_count.should == 1 }
    expect { assigns(:message).should_not be_new_record }
    expect { @user.should_not be_message_sending_blocked }
    expect { assert_no_email_to_administrator }

    should "create and send" do
      assert_sent_email do |email|
        email.to.first == @recipient.email &amp;&amp;
        email.from.first == "do_not_reply@singlestravelintl.com"
      end
    end

  end

### Outer context continues
</code></pre><p>What to say about these tests&#8230;</p><ul><li>In controller tests, I organize contexts by action. The outer context setup clears the email and creates a couple of users &#8212; the <code>login!</code> method creates a factory user and simulates a login.</li><li>The internal context posts the message</li><li>The various <code>expect</code> blocks each resolve into a test that passes if the  block returns true. The first one checks that a message has actually gone into the database (<code>messages_sent</code> is an association on user). The <code>spam_message_count</code> is what is used to determine if a user is blocked &#8212; that&#8217;s not going to pass yet, because the concept of a spam count is new to the app. The third tests that the message assigned in the controller is actually saved. The fourth checks the <code>message_sending_blocked?</code> method of a user, which is also going to need to be written. The last calls a helper method to determine if an email is sent to the administrator, which should only happen if the user is blocked.</li></ul><p>Many of these tests pass as is. The ones that don&#8217;t are dependent on the new user features. Which means we need user tests.</p><p>I realize that some of you are starting to think that having all these tests is ridiculous overkill &#8212; I mean, you&#8217;ve got your Cucumber tests, you&#8217;ve got your controller tests, and now model tests. That&#8217;s, like, triple the work, isn&#8217;t it?</p><p>No, I don&#8217;t think it is. My rationale for doing all this testing goes something like this:</p><ul><li>In my head &#8212; which is, admittedly a strange place to be &#8212; these are all different tests covering different aspects of the same feature. They could all fail independently of each other, at least in theory. Each of them is exercising a different part of the code, and crucially, each of them is  responsible for potentially testing error conditions at in their own space.</li><li>It&#8217;s not that much extra time &#8212; I&#8217;m presenting the code in larger chunks than I actually wrote it (can you imagine how long this would be otherwise?). In the rhythm of testing, it&#8217;s a very short piece of test, followed by a very short bit of code, and the amount of tests grows over time.</li></ul><p>Anyway, the user tests cover the case where a user has four messages, and then the transition to five messages. The setup is pretty similar (another reason why the extra tests don&#8217;t take as long to write as you might think)</p><pre><code>context "rate limiting" do

  setup do
    ActionMailer::Base.deliveries.clear
    Timecop.freeze(Date.today)
    @user = Factory.create(:user, :created_at =&gt; 1.month.ago)
    4.times do |i|
      Message.new_from_params(
          Factory.attributes_for(:message,
              :created_at =&gt; i.hours.ago, :body =&gt; "Message #{i + 1}"),
          @user, Factory.create(:user)).save!
    end
  end

  should "correctly count messages" do
    @user.spam_message_count.should == 4
    @user.update_message_block_status.should be_nil
    @user.should be_able_to_send_messages
    assert_number_of_emails_to_administrator(0)
  end

  should "move user to blocked mode after fifth message" do
    Message.new_from_params(Factory.attributes_for(:message,
        :created_at =&gt; 10.minutes.ago), @user,
        Factory.create(:user)).save!
    @user.update_message_block_status.should be("blocked")
    @user.should_not be_able_to_send_messages
    assert_number_of_emails_to_administrator(1)
  end

## Outer context continues
</code></pre><p>Most of this should be clear given all my blabbering so far (and I have no real clear reason for abandoning Zebra in this section of tests). One thing is that the method <code>update_message_block_status</code> is actually responsible for changing the user status after a message is sent.</p><p>This is getting way long, so <a href="http://www.pathf.com/blogs/2009/07/real-testing-example-part-two/">back next</a> with with how this played out with new requirements, and a kind of interesting bug.</p><p>Quibbles with my testing style or process should go in the comments.</p><p>Related Services: <a href="http://www.pathf.com/services/technology-expertise/ruby-on-rails/">Ruby on Rails Development</a>, <a href="http://www.pathf.com/services">Custom Software Development</a><p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;linkname=A%20Real%20Testing%20Example" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2009%2F07%2Fa-real-testing-example%2F&amp;title=A%20Real%20Testing%20Example" id="wpa2a_20">Share/Bookmark</a></p> ]]></content:encoded> <wfw:commentRss>http://pathfindersoftware.com/2009/07/a-real-testing-example/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using memcached

Served from: pathfindersoftware.com @ 2012-02-08 16:22:24 -->
