<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Nick Sieger: Tag testing</title>
  <id>tag:blog.nicksieger.com,2005:Typo</id>
  <generator uri="http://www.typosphere.org" version="4.0">Typo</generator>
  <link rel="self" type="application/atom+xml" href="http://blog.nicksieger.com/xml/atom10/tag/testing/feed.xml"/>
  <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/tag/testing?tag=testing"/>
  <updated>2007-07-13T09:45:26+00:00</updated>
  <entry>
    <author>
      <name>Nick Sieger</name>
    </author>
    <id>urn:uuid:97cbdf32-ed82-430e-a874-f7fd98b2e999</id>
    <published>2007-06-11T15:35:00+00:00</published>
    <updated>2007-07-13T09:45:26+00:00</updated>
    <title>Test Your Rake Tasks</title>
    <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/2007/06/11/test-your-rake-tasks"/>
    <category term="ruby" scheme="http://blog.nicksieger.com/articles/tag/ruby"/>
    <category term="testing" scheme="http://blog.nicksieger.com/articles/tag/testing"/>
    <content type="html">&lt;p&gt;Many libraries and plugins ship custom Rake tasks.  Of course, as slick as Rake is for a build and configuration language, it&amp;#8217;s still just Ruby code right?  &lt;/p&gt;

&lt;p&gt;Case in point: I released a version of &lt;a href="http://caldersphere.rubyforge.org/ci_reporter"&gt;&lt;code&gt;ci_reporter&lt;/code&gt;&lt;/a&gt; with a fairly careless bug in a rake task that attempted to &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; a string into an existing environment variable.  It escaped me at the time that Ruby sets up the &lt;code&gt;ENV&lt;/code&gt; hash with frozen strings, because my own usage of ci_reporter did not exercise the task in that way.&lt;/p&gt;

&lt;p&gt;So shouldn&amp;#8217;t that Ruby code be subjected to the rigor of automated testing just like the rest of your code?  It became obvious to me that it must be so.  It turns out it&amp;#8217;s straightforward to use Rake in an embedded fashion, and invoke targeted tasks in your custom Rake recipes.  The examples here use RSpec, since that&amp;#8217;s what I use for testing &lt;code&gt;ci_reporter&lt;/code&gt;, but you could apply this to &lt;code&gt;Test::Unit&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;The technique is to create a new instance of &lt;code&gt;Rake::Application&lt;/code&gt;, make it the active application, and load your rake scripts into it:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;ci_reporter ci:setup:testunit task&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;before&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:each&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@rake&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Application&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
    &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;application&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@rake&lt;/span&gt;
    &lt;span class="ident"&gt;load&lt;/span&gt; &lt;span class="constant"&gt;CI_REPORTER_LIB&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/ci/reporter/rake/test_unit.rb&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;after&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:each&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;application&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="comment"&gt;# ...&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the use of &lt;code&gt;#load&lt;/code&gt; rather than &lt;code&gt;#require&lt;/code&gt;, as you want to execute your rake script each time you setup the Rake application object.  When tearing down your test or example, you should cleanup Rake by setting the &lt;code&gt;Rake.application&lt;/code&gt; back to nil (or save the previous application and restore it, if you prefer).&lt;/p&gt;

&lt;p&gt;Now, in the body of your test or example, you invoke your rake task with &lt;code&gt;@rake['target'].invoke&lt;/code&gt;.  Here, I&amp;#8217;m exercising the case of an existing, frozen &lt;code&gt;ENV&lt;/code&gt; value.  After the task is invoked, I check the value after the task to make sure the variable was modified as expected.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should append to ENV['TESTOPTS'] if it already contains a value&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;TESTOPTS&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;somevalue&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;freeze&lt;/span&gt;
  &lt;span class="attribute"&gt;@rake&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;ci:setup:testunit&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;].&lt;/span&gt;&lt;span class="ident"&gt;invoke&lt;/span&gt;
  &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;TESTOPTS&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;].&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;somevalue.*test_unit_loader&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I was fortunate here that the tasks for which I wrote tests after the fact were simple enough to be testable on their own, which may not always be the case, especially with organic, homegrown Rake tasks that interact with the world outside of Ruby.  Still, if your Rake tasks are a critical part of your application, library or plugin, they should be tested.  For example, it would be nice if tests could be written for the Rake scripts in Rails&amp;#8217; Railties module to increase coverage there.&lt;/p&gt;

&lt;p&gt;Perhaps someone out there will run with this idea and take up the challenge and write a Rakefile completely in a test-driven or behaviour-driven style.  It&amp;#8217;s always been a sore point for me with Make, Ant, Maven, and virtually every other build tool in existence that you have no other way of automatically verifying your build script is doing what you intended without manually running it and inspecting its output &amp;#8211; it just feels so dirty!  I&amp;#8217;d expect that test-driven Rake scripts would likely have the level of granularity to match the tasks that need to be done, in a way that you can combine them in the right ways to make incremental and deconstructed builds simpler.&lt;/p&gt;</content>
  </entry>
  <entry>
    <author>
      <name>Nick Sieger</name>
    </author>
    <id>urn:uuid:52bf2e58-ea0e-4473-bad5-c7b42775bfee</id>
    <published>2006-09-13T20:35:00+00:00</published>
    <updated>2007-08-31T17:25:45+00:00</updated>
    <title>Auto RSpec</title>
    <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/2006/09/13/auto-rspec"/>
    <category term="testing" label="testing" scheme="http://blog.nicksieger.com/articles/category/testing"/>
    <category term="ruby" label="ruby" scheme="http://blog.nicksieger.com/articles/category/ruby"/>
    <category term="rails" label="rails" scheme="http://blog.nicksieger.com/articles/category/rails"/>
    <category term="ruby" scheme="http://blog.nicksieger.com/articles/tag/ruby"/>
    <category term="autotest" scheme="http://blog.nicksieger.com/articles/tag/autotest"/>
    <category term="rspec" scheme="http://blog.nicksieger.com/articles/tag/rspec"/>
    <category term="testing" scheme="http://blog.nicksieger.com/articles/tag/testing"/>
    <content type="html">&lt;p&gt;&lt;em&gt;Update: (2 months later) If you&amp;#8217;re reading this, you&amp;#8217;re probably interested in &lt;a href="/articles/2006/11/15/rspec-autotest-now-a-rails-plugin"&gt;my Rails plugin for this&lt;/a&gt; instead.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hot off the presses, after a few hours of hacking and tweaking, may I present Auto+RSpec, otherwise known as The Mashup of &lt;a href="http://rspec.rubyforge.org/tools/rails.html"&gt;RSpec on Rails&lt;/a&gt; and &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;autotest&lt;/a&gt;.  This is not an official release of any sort, but &amp;#8220;may work for you.&amp;#8221;  It&amp;#8217;s not a clean hack, as it exposes some areas for autotest to grow if the maintainers decide to open it up to alternatives to Test::Unit.  After spending a little time looking at the autotest code, I think it would be nice to allow hooks for autotest plugins to define project conventions (i.e., &lt;code&gt;@exceptions&lt;/code&gt; and the &lt;code&gt;#tests_for_file&lt;/code&gt; method) as well as a result parsing API.&lt;/p&gt;

&lt;p&gt;For now, if you&amp;#8217;re an RSpec on Rails user, you can try this out as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="http://www.zenspider.com/ZSS/Products/ZenTest/"&gt;ZenTest&lt;/a&gt; if you haven&amp;#8217;t already: &lt;code&gt;sudo gem install ZenTest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Download &lt;a href="http://svn.caldersphere.net/svn/main/rspec_autotest/trunk/lib/rspec_autotest.rb"&gt;rspec_autotest.rb&lt;/a&gt; and put in your &lt;code&gt;vendor/plugins/rspec/lib&lt;/code&gt; directory (you did say you&amp;#8217;re using RSpec on Rails didn&amp;#8217;t you?)&lt;/li&gt;
&lt;li&gt;Download &lt;a href="http://svn.caldersphere.net/svn/main/rspec_autotest/trunk/tasks/rspec_autotest.rake"&gt;rspec_autotest.rake&lt;/a&gt; and put in your &lt;code&gt;lib/tasks&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Start &lt;code&gt;autotest&lt;/code&gt; with rake by typing &lt;code&gt;rake spec:autotest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Note: if you&amp;#8217;re using RSpec 0.6, you might have better success with &lt;a href="http://svn.caldersphere.net/svn/main/rspec_autotest/tags/rspec-0.6.0"&gt;the files located here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next steps for this will be to work out whether this code should live in RSpec on Rails or autotest, or some combination of those.&lt;/p&gt;

&lt;p&gt;Now, spec&amp;#8217;ers, be off in search of that &lt;strong&gt;Red/Green/Refactor&lt;/strong&gt; rhythm of which sage agilists speak!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus tip&lt;/strong&gt;: add the following code to your &lt;code&gt;.autotest&lt;/code&gt; file to run &lt;code&gt;spec&lt;/code&gt; with &lt;code&gt;rcov&lt;/code&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="constant"&gt;Autotest&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;add_hook&lt;/span&gt; &lt;span class="symbol"&gt;:initialize&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;at&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="comment"&gt;# run spec with rcov&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;at&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;respond_to?&lt;/span&gt; &lt;span class="symbol"&gt;:spec_command&lt;/span&gt;
      &lt;span class="ident"&gt;at&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;spec_command&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;%{&lt;/span&gt;&lt;span class="string"&gt;rcov --exclude &amp;quot;lib/spec/.*&amp;quot; -Ilib --rails &amp;quot;/usr/lib/ruby/gems/1.8/gems/rspec-0.6.0/bin/spec&amp;quot; -- --diff&lt;/span&gt;&lt;span class="punct"&gt;}&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
  </entry>
</feed>
