<?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>Pervasive Code &#187; testing</title>
	<atom:link href="http://www.pervasivecode.com/blog/category/testing/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.pervasivecode.com/blog</link>
	<description>Jamie Flournoy's Software Development Blog</description>
	<lastBuildDate>Mon, 26 Jul 2010 05:29:53 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>The Princess and the Pea, as a Cucumber Feature</title>
		<link>http://www.pervasivecode.com/blog/2010/06/01/the-princess-and-the-pea-as-a-cucumber-feature/</link>
		<comments>http://www.pervasivecode.com/blog/2010/06/01/the-princess-and-the-pea-as-a-cucumber-feature/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 20:00:41 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=197</guid>
		<description><![CDATA[Kent Beck tweeted:

User story: &#8220;As a princess I want to confirm my royalty so I get bruised after sleeping on 40 mattresses over a pea&#8221;. Just tell real stories&#8221;

That sounded so much like a Cucumber feature that I decided to write it as one:

Feature: Physical Sensitivity
  In order to confirm my royalty
  As [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://twitter.com/kentbeck/">Kent Beck</a> tweeted:</p>
<blockquote><p>
User story: &#8220;As a princess I want to confirm my royalty so I get bruised after sleeping on 40 mattresses over a pea&#8221;. Just tell real stories&#8221;
</p></blockquote>
<p>That sounded so much like a <a href="http://cukes.info/">Cucumber</a> feature that I decided to write it as one:</p>
<pre>
Feature: Physical Sensitivity
  In order to confirm my royalty
  As a princess
  I want to be very delicate

  Scenario: 40 mattresses on a pea
  Given there is a pea on the bed
  And there is a stack of 20 mattresses on the pea
  And there is a stack of 20 featherbeds on the mattresses
  When I try to sleep on top of the stack of featherbeds
  Then I should not be able to sleep
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2010/06/01/the-princess-and-the-pea-as-a-cucumber-feature/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to make Machinist and Autotest coexist</title>
		<link>http://www.pervasivecode.com/blog/2010/03/23/how-to-make-machinist-and-autotest-coexis/</link>
		<comments>http://www.pervasivecode.com/blog/2010/03/23/how-to-make-machinist-and-autotest-coexis/#comments</comments>
		<pubDate>Wed, 24 Mar 2010 03:47:22 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[agile development]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=189</guid>
		<description><![CDATA[If you&#8217;ve tried to use Machinist and autotest (part of ZenTest) you have probably seen this exception that prevented you from using it: 
`method_missing': No sham defined for name
It&#8217;s discussed in the machinist Google Group as well.
It&#8217;s because of a wacky hack that&#8217;s part of Machinist that overrides Module.name so you can do Sham.name, but [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve tried to use Machinist and autotest (part of ZenTest) you have probably seen this exception that prevented you from using it: </p>
<pre>`method_missing': No sham defined for name</pre>
<p>It&#8217;s discussed in <a href="http://groups.google.com/group/machinist-users/browse_thread/thread/9af4aa2243ae0368/4e9bba83113ace20">the machinist Google Group</a> as well.</p>
<p>It&#8217;s because of a wacky hack that&#8217;s part of Machinist that overrides Module.name so you can do <code>Sham.name</code>, but ZenTest expects Module.name to do what it does normally.</p>
<p>I have a fix for this.<br />
<span id="more-189"></span></p>
<p>I sent notahat a pull request via GitHub, so hopefully it&#8217;ll be merged by notahat into the official machinist gem, but until that happens you can try mine. I published the gem as JamieFlournoy-machinist to rubygems.org.</p>
<p>Try &#8216;gem install JamieFlournoy-machinist -v 1.0.6&#8242; and see if it works for you. It does for me with ZenTest 4.2.1, autotest-rails 4.1.0, and a Rails 2.3.5 project.</p>
<p><a href="http://github.com/JamieFlournoy/machinist">(my machinist fork on github)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2010/03/23/how-to-make-machinist-and-autotest-coexis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using RVM to install REE 1.8.7-2009.10</title>
		<link>http://www.pervasivecode.com/blog/2010/03/16/using-rvm-to-install-ree-1-8-7-2009-10/</link>
		<comments>http://www.pervasivecode.com/blog/2010/03/16/using-rvm-to-install-ree-1-8-7-2009-10/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 01:48:53 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=160</guid>
		<description><![CDATA[I&#8217;m trying out Ruby Version Manager this week, and my first impression is that this is some cool technology. But I wasn&#8217;t able to figure out how to get it to install an older version of REE to get around this bug (the &#8220;Marshal.load reentered at marshal_load&#8221; issue).
Igor P&#8217;s solution is correct (just install REE [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m trying out <a href="http://rvm.beginrescueend.com/">Ruby Version Manager</a> this week, and my first impression is that this is some cool technology. But I wasn&#8217;t able to figure out how to get it to install an older version of REE to get around <a href="http://groups.google.com/group/emm-ruby/browse_thread/thread/ba3ef0a931618052">this bug</a> (the &#8220;Marshal.load reentered at marshal_load&#8221; issue).</p>
<p>Igor P&#8217;s solution is correct (just install REE 1.8.7-2009.10), but it took a little fiddling to figure out how to get RVM to use the older version of REE. Here&#8217;s how to do it:</p>
<pre>
cd ~/.rvm/archives
wget -q http://rubyforge.org/frs/download.php/66162/ruby-enterprise-1.8.7-2009.10.tar.gz
rvm install ree-1.8.7-2009.10
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2010/03/16/using-rvm-to-install-ree-1-8-7-2009-10/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Proper Error Handling in Rails Controllers</title>
		<link>http://www.pervasivecode.com/blog/2009/07/27/proper-error-handling-in-rails-controllers/</link>
		<comments>http://www.pervasivecode.com/blog/2009/07/27/proper-error-handling-in-rails-controllers/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 04:56:24 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=131</guid>
		<description><![CDATA[Rails controllers can get out of hand if you&#8217;re not very careful. Skinny Controller Fat Model is a great start. But what about handling errors? Isn&#8217;t it enough to just let Rails catch your exception and show a 500 Server Error page?
No, it&#8217;s not. Falling back on 500 Server Error for everything outside of the [...]]]></description>
			<content:encoded><![CDATA[<p>Rails controllers can get out of hand if you&#8217;re not very careful. <a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model">Skinny Controller Fat Model</a> is a great start. But what about handling errors? Isn&#8217;t it enough to just let Rails catch your exception and show a 500 Server Error page?</p>
<p>No, it&#8217;s not. Falling back on 500 Server Error for everything outside of the &#8220;happy path&#8221; through your code is sloppy coding.<br />
<span id="more-131"></span><br />
I say this for three reasons:</p>
<p><b>The 5xx response codes are for server-side errors.</b></p>
<p>If the code is working properly but the request is wrong (no such document, an invalid request, or insufficient user privileges for the requested operation), the 4xx response codes are more appropriate. You should only return a 5xx series error message when your application is broken.</p>
<p><b>A generic &#8220;oops&#8221; page hides the cause of the error from the user.</b></p>
<p>Maybe the user followed a bad link, that goes to a document that has been deleted. They should see a 404 Not Found response, not a 500 Server Error. This is especially important if the user agent is a web service client actively under development. And remember that you can still customize the response body, so a 404 can show a search or site map to help them find the content that was Not Found, and a 403 might suggest that the user upgrade their subscription so they can see the Forbidden content.</p>
<p><b>Failing to trap bogus input is a recipe for a security vulnerability.</b></p>
<p>Rails will detect ridiculous input in many cases, but it doesn&#8217;t know about your business rules.</p>
<p>For example, what if someone fires up Firebug and figures out your API, and deletes her ex-boyfriend&#8217;s account because you forgot to check that the ID being deleted matches the currently logged in user? Oops.</p>
<p>You know who else got pwn3d because they put their security rules in the client? AOL. Don&#8217;t be like AOL. They got <a href="http://en.wikipedia.org/wiki/AOHell">pwn3d big time</a>. Make sure your controller handles the &#8220;unhappy path&#8221; too, and responds appropriately.</p>
<p>You make sure your controller handles these cases by writing tests, and checking that the response is correct. To do this, you need unambiguous errors that clearly explain what the controller didn&#8217;t like about the request. </p>
<p>Your controller tests should <i>never</i> expect an exception. Exceptions belong inside the application (such as between a model and a controller), not at the boundary.</p>
<p>Instead, you should be using something like <code>assert_response :bad_request</code>, and providing a useful textual message in the page that will help the user understand what they did wrong.</p>
<p>A nice bonus of using the right HTTP error responses is that your tests are more readable. And since controller tests tend to be really verbose already (mine tend to be ~10x as many LOC as the controller itself), the extra readability is really nice.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2009/07/27/proper-error-handling-in-rails-controllers/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>A Cucumber step to test for a YM4R Google Map</title>
		<link>http://www.pervasivecode.com/blog/2009/07/27/a-cucumber-step-to-test-for-a-ym4r-google-map/</link>
		<comments>http://www.pervasivecode.com/blog/2009/07/27/a-cucumber-step-to-test-for-a-ym4r-google-map/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 03:34:46 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[Google Maps]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=116</guid>
		<description><![CDATA[I had some problems with a view in a Rails app that was conditionally hiding a Google Map that was generated using the YM4R plugin. I don&#8217;t usually test views in unit tests, and this logic depended on a particular situation with the data behind the view, so I decided that this would be a [...]]]></description>
			<content:encoded><![CDATA[<p>I had some problems with a view in a Rails app that was conditionally hiding a Google Map that was generated using the <a href="http://ym4r.rubyforge.org/">YM4R</a> plugin. I don&#8217;t usually test views in unit tests, and this logic depended on a particular situation with the data behind the view, so I decided that this would be a good candidate for a Cucumber feature.</p>
<p>Here&#8217;s the Cucumber step implementation I wrote.<br />
<span id="more-116"></span><br />
Put this in a new file called features/step_definitions/ym4r_gm_steps.rb :</p>
<pre>
# Steps for use with the ym4r_gm plugin

Then /^I should see a Google Map$/i do
  within 'head' do
    ['http://maps.google.com/maps', '/javascripts/ym4r-gm.js'].each do |js_url|
      assert_have_xpath "//script[starts-with(@src, '#{js_url}')]"
    end
  end
  assert_select '#map_div'
end
</pre>
<p>This assumes that you let the plugin installer put the .js file in the default location, and that you have your Google Map inside #map_div.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2009/07/27/a-cucumber-step-to-test-for-a-ym4r-google-map/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fancier Stubbing of GeoKit for Rails unit tests</title>
		<link>http://www.pervasivecode.com/blog/2009/07/23/fancier-stubbing-of-geokit-for-rails-unit-tests/</link>
		<comments>http://www.pervasivecode.com/blog/2009/07/23/fancier-stubbing-of-geokit-for-rails-unit-tests/#comments</comments>
		<pubDate>Fri, 24 Jul 2009 00:00:44 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[process]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[servers]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=108</guid>
		<description><![CDATA[I&#8217;m working on a Rails app that uses the ym4r_gm plugin, getting Google to do the geocoding for Thentic. I liked the idea of stubbing the web service call, because all those calls to an external service add up to over 20 seconds of test suite run time(!). That&#8217;s almost half of the 50 second [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on a Rails app that uses the ym4r_gm plugin, getting Google to do the geocoding for <a href="http://www.thentic.com/">Thentic</a>. I liked the idea of stubbing the web service call, because all those calls to an external service add up to over 20 seconds of test suite run time(!). That&#8217;s almost half of the 50 second run time of my unit tests (and 50 seconds is much too long for a unit test suite).</p>
<p>I found a good starting point at <a href="http://beardendesigns.com/blogs/permalink/55">geokit stubbing for faster tests</a>. I also wanted a way to stub a geocoding failure, and a way to prevent any unit tests from using the real geocoding web service.</p>
<p>Here&#8217;s how I did it.<br />
<span id="more-108"></span><br />
In test/test_helper.rb, add this:</p>
<pre>
class ActiveSupport::TestCase
  def setup
    GeoKit::Geocoders::MultiGeocoder.stubs(:geocode).raises(RuntimeError,
      'Use mock_geocoding_success! or mock_geocoding_failure! in your test')
  end

  def mock_geocoding_success!
    geocode_payload = GeoKit::GeoLoc.new(:lat => 123.456, :lng => 123.456)
    geocode_payload.success = true
    GeoKit::Geocoders::MultiGeocoder.expects(:geocode).returns(geocode_payload)
  end

  def mock_geocoding_failure!
    geocode_payload = GeoKit::GeoLoc.new
    geocode_payload.success = false
    GeoKit::Geocoders::MultiGeocoder.expects(:geocode).returns(geocode_payload)
  end
end
</pre>
<p>What this does is to force you to choose either one of those mock_geocoding methods before you call the geocode method. To me this seems like a good idea since the integration tests that exercise the full application stack should probably be written using Cucumber and Webrat (which is what I&#8217;m using).</p>
<p>You will probably want to merge my one-line setup method into your existing setup code in test_helper, if any. Also note that this uses <a href="http://mocha.rubyforge.org/">Mocha</a> for mocking.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2009/07/23/fancier-stubbing-of-geokit-for-rails-unit-tests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Things I had to fix for Rails 2.2.2</title>
		<link>http://www.pervasivecode.com/blog/2008/11/29/things-i-had-to-fix-for-rails-222/</link>
		<comments>http://www.pervasivecode.com/blog/2008/11/29/things-i-had-to-fix-for-rails-222/#comments</comments>
		<pubDate>Sun, 30 Nov 2008 01:57:06 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/?p=85</guid>
		<description><![CDATA[The new features in Ruby on Rails 2.2.2 have been well documented, and I&#8217;m looking forward to using several of them on WhatYouAte.com. If you&#8217;re reading this you probably are too.
However, if you&#8217;re upgrading an existing project and you&#8217;re sticking with official releases (as opposed to edge Rails) like I am, your code probably needs [...]]]></description>
			<content:encoded><![CDATA[<p>The new features in Ruby on Rails 2.2.2 have been <a href="http://www.rubyinside.com/rails-22-released-27-links-and-resources-to-get-you-going-1354.html">well documented</a>, and I&#8217;m looking forward to using several of them on <a href="http://www.whatyouate.com/">WhatYouAte.com</a>. If you&#8217;re reading this you probably are too.</p>
<p>However, if you&#8217;re upgrading an existing project and you&#8217;re sticking with official releases (as opposed to edge Rails) like I am, your code probably needs some tweaking to work with Rails 2.2.2. Mine certainly did. Although there were a lot of failed tests with ugly stacktraces, there were only a few API changes in Rails that needed to be accomodated to fix them all. Here&#8217;s a list of the changes that broke my app, and what I had to do to get it working again.<br />
<span id="more-85"></span></p>
<p><code>TextHelper.truncate</code> no longer allows you to supply the length parameter as a second argument. You have to use <code>truncate(somestring, :length => 20)</code> instead of <code>truncate(somestring, 20)</code>.</p>
<p>I was getting an error that looked like this:</p>
<pre>ActionView::TemplateError: wrong number of arguments (3 for 1)</pre>
<p>The line in question is:</p>
<pre>
&lt;%= render(:partial => 'layouts/header') %&gt;
</pre>
<p>The problem is that the simply_helpful plugin&#8217;s behavior is now built-in; just delete the whole vendor/plugins/simply_helpful directory if you were using it, and any existing code that depends on simply_helpful&#8217;s API should work again.</p>
<p>I was using the <a href="http://weblog.jamisbuck.org/2007/2/2/introducing-tztime">tztime</a> and <a href="http://agilewebdevelopment.com/plugins/tzinfo_timezone">tzinfo_timezone</a> plugins, which are no longer needed now that time zone support is available via Time.zone in Rails 2.2. Delete the plugins, upgrade the tzinfo gem to 0.3.12 or uninstall any older version of it, and search for TzTime instances in your code and replace any of the occurences of Time.at(some_tztime_variable) that you had been using. TzInfo is still useful in cases such as this:</p>
<pre>TZInfo::Timezone.get('UTC')</pre>
<p>But since you can now embed the time zone information in a Time instance, you don&#8217;t need TzTime objects anymore.</p>
<p>I was using a nice hack from Technoweenie, to allow me to validate forms using the familiar ActiveRecord model validations API even if those forms were not backed by a database table (sometimes the UI just doesn&#8217;t look anything like the data model, y&#8217;know?). It broke, and I was unable to find any solutions from anyone else, so I wrote my own: <a href="http://www.pervasivecode.com/blog/2008/11/29/ephemeralmodel-for-rails-222-form-validation-without-a-db-table/">EphemeralModel, for Rails 2.2.2 form validation without a DB table</a>.</p>
<p>I was using <code>ActiveRecord::Errors.default_error_messages[:invalid]</code> in a few model validators, which is now deprecated because it&#8217;s not internationalized. The new way to ask for the same string in the currently configured language is: <code>I18n.t(:invalid, :scope => 'activerecord.errors.messages')</code></p>
<p>Finally, the app wouldn&#8217;t start because it wants a <code>:secret</code> option to <code>config.action_controller.session</code> in <code>config/environment.rb</code>. I came up with something sufficiently random and now it&#8217;s fine.</p>
<p>I&#8217;d just like to make one last comment about this: having thorough automated tests is a huge time and sanity saver during this process. If you&#8217;re not close to 100% test coverage on your current Rails project, then your house is on fire and you need to address that immediately. Even as I write this in late 2008, I&#8217;m talking to prospective clients about contracting work, and some of them don&#8217;t do automated testing at all(!). Unless you&#8217;re in a mad dash for a demo that will save the company from financial collapse, having automated tests that verify that your code works should be priority #1. Upgrades can wait.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2008/11/29/things-i-had-to-fix-for-rails-222/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Techniques for Exhaustively Testing a Rails App</title>
		<link>http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/</link>
		<comments>http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/#comments</comments>
		<pubDate>Sat, 23 Aug 2008 22:55:12 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[coverage]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/</guid>
		<description><![CDATA[I subscribe to dozens of tech blogs (including but not limited to Ruby and Rails), and although I&#8217;ve seen quite a lot of commentary about TDD and BDD, I&#8217;m not sold on either of them yet. TDD is interesting, but BDD seems like a waste of time. But I am completely sold on automated testing [...]]]></description>
			<content:encoded><![CDATA[<p>I subscribe to dozens of tech blogs (including but not limited to Ruby and Rails), and although I&#8217;ve seen quite a lot of commentary about TDD and BDD, I&#8217;m not sold on either of them yet. TDD is interesting, but BDD seems like a waste of time. But I <em>am</em> completely sold on automated testing in general. I write lots and lots of tests and make sure using rcov that my tests cover all of the code. </p>
<p>Getting 100% coverage isn&#8217;t easy. In general it means you definitely can&#8217;t just write a single test case per method and declare victory when it passes. When the number of possible combinations of inputs (method arguments and/or mock objects) and expected outputs (return values, exceptions, and side effects) becomes large, then the potential for copy-and-paste errors in your test code becomes large, and legibility becomes an issue. This is the point at which I find the recent fascination with writing tests in a near-natural-language DSL to be a distraction. It&#8217;s orthogonal to the problem I&#8217;m dealing with, which is how to comprehensively test the code. In other words, the problem is not making a small number of tests more readable, but concisely expressing a large number of input/output combinations and a large number of different tests, and making it all readable.</p>
<p>I assume that there are others working on this problem too, so I&#8217;ll describe some of the things I&#8217;ve come up with, and hopefully if you have any good ideas you&#8217;ll post them in the comments section.</p>
<p><span id="more-78"></span></p>
<p><strong>Test Data Iterators</strong></p>
<p>If you express the set of inputs and outputs as a data structure, such as a Hash of input => expected ouput pairs, or an Array of [input, expected output] Arrays, you can easily do <code>test_data.each do |input,output| ... end</code> and drive tests that way.</p>
<p>(I considered pulling the data itself out of the test code and loading it from a YAML or CSV file or even including a data-only Ruby script, but that seemed like pointless added complexity. If I were working with a professional tester who didn&#8217;t want to touch Ruby code, I might change that to make it easier to dump from an Excel spreadsheet into a text file that the tests would use.)</p>
<p>One problem with this approach is that you may have a <code>test_frobnicate</code> method that invokes <code>frobnicate</code> dozens of times. This means that this one test can take a while to run if the tested method takes any amount of time, which is problematic if you just want to re-test that one input/output combo until it works. Your edit/test/debug loop should be ultra fast, and re-testing successful pairs gets in the way of testing the pair you&#8217;re not sure about.</p>
<p>More importantly, trying all those combinations in one test method means that if an assertion fails, the failure masks the outcome of the remaining combinations. If there are 25 combinations and combination #2 fails, then a <code>Test::Unit::AssertionFailedError</code> is thrown, and the other 23 of them go unchecked. That&#8217;s not ideal, since it can be useful in debugging if you can see that 24/25 failed vs. 1/25 failed.</p>
<p>To solve this, I changed the code so that it iterates over the test data inside the class definition, generating a new test method for each input/output pair:</p>
<pre>
class_eval do
  test_data_hash.each do |input, output|
    define_method("test_frobnicator_#{input.hash}") do
      check_frobnicator(input, output)
    end
  end
end
</pre>
<p>That gives you a bunch of test methods that each run the test once with different input and expected output values. Autotest integrates well with this technique, since it will not re-run the test methods which previously passed until after you have fixed all of the ones which failed.</p>
<p><strong>Test Setup Blocks</strong></p>
<p>I also use a <code>with_x_y_z do...end</code> idiom for mock setup when possible. So a test might contain:</p>
<pre>
def check_index(inputs, expected_outputs)
  if inputs[:logged_in]
    with_fake_login{|user| get :index, inputs[:params] }
  else
    get :index, inputs[:params]
  end

  # ... test outputs against expected output
end
</pre>
<p>Since this idiom uses blocks, you can nest them to create complicated setup scenarios, pass arguments to the <code>with_blah</code> method to control the behavior of the mock objects, and so on. (You could do this with regular <code>set_up_blah</code> methods but I think this is easier to read.)</p>
<p><strong>Calculated Outputs</strong></p>
<p>Despite their shortcomings, I still use Fixtures (with the PreloadFixtures plugin) and I write unit tests for my models. That means that I can then assume that the models&#8217; behavior is correct in other tests (provided that those unit tests pass). So I try not to put any data that&#8217;s already expressed in fixture files into my test method inputs or expected outputs. Instead, I ask for the fixture by its label (<code>widgets(:doodad)</code>) and then use its properties as needed in assertions within the current test. This reduces the size of the input/output data, which makes it easier to read and reduces the amount of effort required to add more combinations later.</p>
<p><strong>Testing Views, Model Associations, and Model Validation</strong></p>
<p>As Bruce Eckel says, <a href="http://www.mindview.net/WebLog/log-0025">If it&#8217;s not tested, it&#8217;s broken.</a> Or as Peter Drucker said, &#8220;What gets measured gets managed.&#8221;</p>
<p>I test all of these. I&#8217;ve read the arguments of those who say that none of these are necessary, and I disagree. The arugment that testing associations and validations is tantamount to testing Rails itself is just incorrect. Rails&#8217; own unit tests cannot possibly test the validations and associations expressed in every Rails application&#8217;s model classes, so Rails application developers still have to do this.</p>
<p><strong>Associations</strong></p>
<p>I can&#8217;t tell you how many times I&#8217;ve gotten has_one and belongs_to backwards and only found out when ActiveRecord told me that the column didn&#8217;t exist, because I told it to look at the table on the wrong side of the association. I feel stupid when I see that I made that mistake, but writing that test takes seconds and once I see the failure it&#8217;s easy to fix. Better to catch it and fix it now rather than have a user find it, right?</p>
<p><strong>Validations</strong></p>
<p>The same goes for model validations. Yes, I know that <code>validates_format_of</code> has been tested, but my regexes for username format and email addresses need testing too.</p>
<p><strong>Views</strong></p>
<p>Views should be tested <em>only if you care what your users see</em>.</p>
<p>Somewhere there is probably an organization with no users, where inputs matter but outputs can be anything. Maybe the goal of the project is to burn cash rather than to ship something good. They don&#8217;t need to test their views.</p>
<p>I agree that views should be dumbed down as much as possible using helpers, but that&#8217;s not enough to assure that they work. If you&#8217;re not testing views with automated tests, then how do you test them? With a browser? Every time you change anything, you&#8217;re going to exhaustively check every combination of inputs and outputs to make sure that all the dynamic goodies are in the right place? I doubt it. Obviously you do have to check views in a browser to make sure that purely visual aspects are correct, but there are things you can check automatically. You can do quite a lot with assert_select, and for client-side tests, <a href="http://selenium.openqa.org/">Selenium</a> can do pretty much anything. (It&#8217;s probably not worth the effort to have Selenium use the DOM to see that the browser applied CSS in exactly the way that you wanted it to, but if you wanted to, you could.)</p>
<p>I don&#8217;t know how to do something similar to coverage testing for views, but a good start is to just check for the effects of everything that the controller stored in instance variables. Maybe those should be displayed as-is in the resulting document, or maybe they should be truncated, or processed by a helper into a different representation.</p>
<p>In any case, you don&#8217;t have to test with the same set of inputs as the functional test for the controller; just do white box testing for the minimal set of combinations that should exercise all of the permutations of the view code (which should be a very small number). Testing the helpers separately can cut down on the number of permutations of inputs and outputs also. So if you have a helper that creates a navigation breadcrumb visual element from an array of strings, you could just test that in isolation with 0, 1, 2, and 3 elements, and then check for one version of breadcrumbs when looking for it in a view.</p>
<p>I&#8217;m particularly inspired by the claim that Sebastian Delmont of <a href="http://www.streeteasy.com/">StreetEasy</a> made in the <a href="http://podcast.rubyonrails.org/programs/1/episodes/sebastian_delmont">May 31, 2006 episode of the Ruby on Rails Podcast</a> about having no human approval step between a successful test suite run and deployment to production. That&#8217;s pretty darn bold, but if you stop and think about it, it is feasible with today&#8217;s tools to automate any &#8220;white glove test&#8221; that a real person would do before approving the release for deployment to production. I&#8217;m not that brave yet, but I like the idea of a condition that checks if the changed code has anything to do with a view (ERB, JavaScript, CSS, etc.), and if not, would just rubberstamp it and deploy it. Clearly a change that could require a manual test in multiple browsers wouldn&#8217;t be a wise thing to autodeploy, but something like fixing a bug deep in the code shouldn&#8217;t require a human to approve it, especially if the autodeploy condition depends on that chunk of code having 100% test coverage following the bug fix.</p>
<p><strong>Your Tips?</strong></p>
<p>I&#8217;m always looking for ways to test code with less effort on my part. Please let me know if you have ideas or suggestions.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2008/08/23/techniques-for-exhaustively-testing-a-rails-app/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Software Project Estimation: Inaccurate and Unavoidable</title>
		<link>http://www.pervasivecode.com/blog/2008/08/06/software-project-estimation-inaccurate-and-unavoidable/</link>
		<comments>http://www.pervasivecode.com/blog/2008/08/06/software-project-estimation-inaccurate-and-unavoidable/#comments</comments>
		<pubDate>Thu, 07 Aug 2008 04:04:04 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[labor]]></category>
		<category><![CDATA[management]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[strategy]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2008/08/06/software-project-estimation-inaccurate-and-unavoidable/</guid>
		<description><![CDATA[This is a follow up to On Our Project, We&#8217;re Always 90% Done.
The coder is the one on the hook for long nights, weekends, and stress-related health problems if the estimates suck. It&#8217;s in your interest to exert as much control over estimation and scheduling as you can. If you&#8217;re not making the estimate, someone [...]]]></description>
			<content:encoded><![CDATA[<p>This is a follow up to <a href="http://www.codinghorror.com/blog/archives/001161.html">On Our Project, We&#8217;re Always 90% Done</a>.</p>
<p>The coder is the one on the hook for long nights, weekends, and stress-related health problems if the estimates suck. It&#8217;s in your interest to exert as much control over estimation and scheduling as you can. If you&#8217;re not making the estimate, someone is making it for you.</p>
<p><span id="more-76"></span></p>
<p>If you think about it, <strong>you&#8217;re opting out of an ongoing salary negotiation process</strong>. You&#8217;re going to work more for the same amount of money, and worse yet, you&#8217;re going to have to squeeze that work into the same amount of time, under stressful conditions. Why would you do that?</p>
<p><a href="http://www.amazon.com/Software-Estimation-Demystifying-Practices-Microsoft/dp/0735605351">McConnell&#8217;s &#8220;Software Estimation&#8221; book</a> addresses this problem of wildly inaccurate early estimates quite well. I highly recommend it. In a nutshell, estimates need to carry error bars, so early estimates would be given but with a disclaimer of +/- 100% (or more!), and later estimates get closer to being correct.</p>
<p>You will have to manage expectations, though. Human nature leads to the shortest end of the date range becoming the remembered end date, the guess being remembered as a promise, and the latest feature set being remembered as if it were the original feature set.</p>
<p>This is a big reason to hire a good project manager: they&#8217;re there to facilitate the change control process, or planning game, or whatever you call it in your organization when the paying folks want more stuff, and the PM reminds them that need to pay more and wait longer in order to get more out of the dev team. A good project manager can make a huge difference to the stress level and likelihood of success of a project.</p>
<p>It&#8217;s psychologically icky for developers to have to participate in the estimation and scheduling process &#8212; &#8220;eww, it&#8217;s a non deterministic domain with lots of ill-defined inputs!&#8221; &#8212; and I&#8217;ve seen some managers play head games like &#8220;Oh, you need more time? I thought you were really good at programming&#8230; I guess not&#8230;&#8221; to try and manipulate developers into working nights and weekends out of a sense of guilt or wounded pride. But you need to suck it up and keep providing status updates and reminding people how the process works, because you-the-programmer are the expert on the nature of programming.</p>
<p>Yes, it&#8217;s more like Lewis and Clark or the Apollo project than like building a house, and there are just going to be a lot of surprises, but you can still provide a framework and exert a tremendous amount of control over how the team will operate. Things like whether you plan to build a technology proof-of-concept to make sure your architecture doesn&#8217;t suck, or whether you intend to spend lots of time writing automated tests and doing daily code reviews in order to control that horrible &#8220;bug fixing&#8221; phase toward the end are in your hands if you participate in the estimation process. You&#8217;re being offered an opportunity to make sure that all those important tasks like performance testing and cross-browser testing and writing an installation guide and an operations manual for the sysadmin get done. <strong>It doesn&#8217;t matter if the estimates for those tasks are completely wrong at first &#8211; what matters is that they&#8217;re on the list.</strong></p>
<p>And really, the largest battle you&#8217;ll probably have to fight is the one over scope creep: we can&#8217;t launch until it has all the original features plus all these additional changes and new features perfectly working, but we need to launch tomorrow. That&#8217;s also human nature, and you need to train people to release early and often. Agile methodologies can help here, if you can get your software working and stable from a very early point in the project, and keep it that way. Then you probably have a launchable product very early, and you can just push highly-desired features into post-1.0 releases.</p>
<p>In any case, a list with a bunch of crappy numbers and low confidence factors is a lot better than no list, or a one-item list with a crappy number and an implied 100% confidence factor.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2008/08/06/software-project-estimation-inaccurate-and-unavoidable/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated .autotest</title>
		<link>http://www.pervasivecode.com/blog/2008/05/17/updated-autotest/</link>
		<comments>http://www.pervasivecode.com/blog/2008/05/17/updated-autotest/#comments</comments>
		<pubDate>Sun, 18 May 2008 05:36:19 +0000</pubDate>
		<dc:creator>Jamie Flournoy</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.pervasivecode.com/blog/2008/05/17/updated-autotest/</guid>
		<description><![CDATA[I just brought a new project into the world of autotest. I&#8217;m not using the Leopard FSEvents &#8220;fix&#8221; because it&#8217;s not necessary (note the sleep and add_exception calls below). I am using the fun and helpful sound plugin, but not the playlist version of that plugin. Here&#8217;s my .autotest file.


Autotest.add_hook :initialize do &#124;autotest&#124;
   [...]]]></description>
			<content:encoded><![CDATA[<p>I just brought a new project into the world of <a href="http://nubyonrails.com/articles/autotest-rails">autotest</a>. I&#8217;m not using the <a href="http://rails.aizatto.com/category/autotest/">Leopard FSEvents &#8220;fix&#8221;</a> because it&#8217;s not necessary (note the sleep and add_exception calls below). I am using the fun and helpful <a href="http://www.fozworks.com/2007/7/28/autotest-sound-effects">sound plugin</a>, but not the <a href="http://www.metaskills.net/2008/4/6/autotest-playlist-for-red-green-feedback">playlist version of that plugin</a>. Here&#8217;s my .autotest file.<br />
<span id="more-73"></span></p>
<pre>
Autotest.add_hook :initialize do |autotest|
    autotest.sleep=3
    autotest.add_exception(/\\/\.git\\//)
end

require '/Users/jamieflournoy/autotest/sound/sound'
Autotest::Sound.sound_app = '/opt/local/bin/mpg321'
Autotest::Sound.sound_path = "/Users/jamieflournoy/autotest/sound/sound_fx/"
</pre>
<p>Note that the sound plugin needs a patch:</p>
<pre>
29c29
<   [:run, :red, :green, :quit, :run_command, :ran_command].each do |hook|
---
>   [:initialize, :red, :green, :quit, :run_command, :ran_command].each do |hook|
</pre>
<p>You&#8217;ll also need to make sure your sound set has an initialize.mp3 instead of a run.mp3.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pervasivecode.com/blog/2008/05/17/updated-autotest/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
