Nick Sieger: Tag rspec tag:blog.nicksieger.com,2005:Typo Typo 2010-11-22T18:06:58+00:00 Nick Sieger urn:uuid:8396524a-7e00-4aad-8727-a5a0b3ec7ef3 2006-11-15T15:46:00+00:00 2010-11-22T18:06:58+00:00 RSpec Autotest now a Rails Plugin <p>Inspired by a <a href="http://rubyforge.org/pipermail/rspec-devel/2006-November/001219.html">posting on the RSpec list</a> and recent <a href="http://blog.nicksieger.com/articles/2006/09/13/auto-rspec#comment-143">comments stating that my Auto RSpec hack wasn&#8217;t working</a>, I&#8217;ve bitten the bullet and upgraded to <a href="http://rspec.rubyforge.org/upgrade.html">RSpec 0&#46;7&#46;2</a>, and made <code>rspec_autotest</code> <a href="http://svn.caldersphere.net/svn/main/plugins/rspec_autotest">a plugin</a> in the process&#46; So, herewith are the necessary incantations to auto&#45;rspec your project&#46; If you&#8217;ve tried my hack already, please remove any bits you previously had installed&#46;</p> <ul> <li>Install RSpec on Rails, following the <a href="http://rspec.rubyforge.org/documentation/rails/install.html">original instructions</a>&#46; As of RSpec 0&#46;7&#46;3, the specific version of ZenTest is no longer required&#46; Also, diff&#45;lcs is required to show unified diff output on <code>should ==</code> failures&#46;</li> </ul> <div class="typocode"><pre><code class="typocode_default ">gem install zentest -v 3.4.1 gem install diff-lcs gem install rspec script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_7_2/vendor/rspec_on_rails/vendor/plugins/rspec</code></pre></div> <ul> <li>Install <code>rspec_autotest</code></li> </ul> <div class="typocode"><pre><code class="typocode_default ">script/plugin install http://svn.caldersphere.net/svn/main/plugins/rspec_autotest</code></pre></div> <ul> <li>Start autotest</li> </ul> <div class="typocode"><pre><code class="typocode_default ">rake spec:autotest</code></pre></div> <p>Please let me know if you experience any problems!</p> Nick Sieger urn:uuid:21755a2d-0ffa-4f0c-be7d-a2e2e01d99b4 2007-01-02T05:32:00+00:00 2010-11-22T18:14:29+00:00 Customizing RSpec <p><em>Update/Disclaimer: I refer to parts of RSpec that are not blessed as an extension API&#46; Redefining <code>before_context_eval</code> and using the <code>@context_eval_module</code> variable directly may change in the future&#46; I&#8217;ll keep this article updated to coincide with the changes&#46; For now, these techniques should work fine with RSpec versions up to 0&#46;7&#46;5&#46;</em></p> <p>RSpec seems to be getting more attention lately as a viable, nay, <em>preferred</em>, alternative to Test::Unit&#46; It&#8217;s possible that it&#8217;s just my personal feed&#45;reader&#45;echo&#45;chamber, but consider this: <a href="http://blog.fallingsnow.net/rubinius/">Rubinius</a> has started using RSpec alongside Test::Unit as an another way to test the alternate Ruby implementation&#46; They&#8217;re even in the midst of building some snazzy extensions to allow the same specs to be run under a Ruby implementation of your choice&#46; (Perhaps this will point the way to a new round of executable specs to accompany the <a href="http://www.headius.com/rubyspec/index.php/Main_Page">fledgling community spec</a>? Let&#8217;s wait and see how they do and leave that topic for another day&#46;)</p> <p>But extending and customizing RSpec to add a DSL on top of RSpec&#8217;s <code>context/specify</code> framework doesn&#8217;t have to be the realm of experts&#46; Here are some templates for how you can DRY up your specs by adding your own helper methods in such a way that they will be available to all your specs&#46; But first, a little background&#46;</p> <h3>Spec Helper</h3> <p>Most usages of RSpec that I&#8217;ve seen in the wild use a &#8220;spec helper&#8221; (<code>spec_helper.rb</code>)&#46; This file, following the pattern of Rails&#8217; <code>test_helper.rb</code>, minimally contains require statements to pull in the RSpec code and any supporting code for running specs&#46; By requiring the spec helper via a path relative to your spec (usually with <code>require File.dirname(__FILE__) + '/spec_helper'</code> or similar), it also allows you the convenience of running your specs one at a time from anywhere (say, by launching from your editor) or with <code>rake</code> or <code>spec</code>&#46; This file is where your shared helper methods will go, and where they&#8217;ll get registered to be pulled into the contexts&#46;</p> <h3>What Context in <code>context</code>?</h3> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A new stack</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="comment"># &lt;== What is the value of &quot;self&quot; here?</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>How do those contexts work anyway? The <code>context</code> method that defines a context in which specs can be defined and run takes a block to define the individual specs, but what can really go in that block?</p> <p>It turns out that RSpec jumps through metaprogramming hoops (using <code>class_eval</code>) to make the block behave like a class definition&#46; This means you can do things like put method definitions inside your context:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A new stack</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="keyword">def </span><span class="method">a_new_stack</span> <span class="constant">Stack</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">end</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">a_new_stack</span><span class="punct">.</span><span class="ident">should_be_empty</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Which is nice, but the reason we&#8217;re here is to hide that away in <code>spec_helper.rb</code>&#46; So, to get back to the point of the comment in the first example above, the <code>self</code> inside the context block is an anonymous <code>Module</code> object&#46; It&#8217;s constructed in the <code>initialize</code> method of a <code>Context</code> (condensed from <code>spec/runner/context.rb</code> in the RSpec codebase):</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Spec::Runner::Context</span> <span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">name</span><span class="punct">,</span> <span class="punct">&amp;</span><span class="ident">context_block</span><span class="punct">)</span> <span class="attribute">@name</span> <span class="punct">=</span> <span class="ident">name</span> <span class="attribute">@context_eval_module</span> <span class="punct">=</span> <span class="constant">Module</span><span class="punct">.</span><span class="ident">new</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">extend</span> <span class="constant">ContextEval</span><span class="punct">::</span><span class="constant">ModuleMethods</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">include</span> <span class="constant">ContextEval</span><span class="punct">::</span><span class="constant">InstanceMethods</span> <span class="ident">before_context_eval</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">class_eval</span><span class="punct">(&amp;</span><span class="ident">context_block</span><span class="punct">)</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">before_context_eval</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>(Take note of that empty <code>before_context_eval</code> method and the fact that it&#8217;s invoked during context initialization; that&#8217;s where we can plug in our custom extensions&#46;)</p> <p>The object held by the <code>@context_eval_module</code> instance variable is being augmented in two ways: extension and inclusion&#46; The object is <em>extended</em> with the <code>ContextEval::ModuleMethods</code> module; these methods are being added to the object&#8217;s singleton class&#46; This has the effect of making these methods visible within the <code>context</code> block, functioning similar to &#8220;class&#8221; methods&#46;</p> <p>The object also has the <code>ContextEval::InstanceMethods</code> module <em>included</em>&#46; This has the effect of adding these as instance methods, making them visible from within <code>specify</code> blocks, which are made to behave like instance methods on the same object&#46;</p> <h3>Putting it together</h3> <table style="text-align: left;" summary=""> <thead> <th>Technique</th> <th>Visibility</th> <th>Use</th> </thead> <tbody> <tr><td>@context_eval_module&#46;extend</td><td>Context block</td><td>Custom setup, shared state declaration</td></tr> <tr><td>@context_eval_module&#46;include</td><td>Specify block</td><td>Shared actions/functions, stub/expectation modification, encapsulate instance variables</td></tr> </tbody> </table> <h4>Adding specialized setup methods</h4> <p><code>spec_helper.rb</code> snippet:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">module </span><span class="module">SharedSetupMethods</span> <span class="keyword">def </span><span class="method">setup_new_stack</span> <span class="ident">setup</span> <span class="keyword">do</span> <span class="attribute">@stack</span> <span class="punct">=</span> <span class="constant">Stack</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">Spec::Runner::Context</span> <span class="keyword">def </span><span class="method">before_context_eval</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">extend</span> <span class="constant">SharedSetupMethods</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Example spec:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A new stack</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">setup_new_stack</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="attribute">@stack</span><span class="punct">.</span><span class="ident">should_be_empty</span> <span class="keyword">end</span> <span class="keyword">end</span> </code></pre></div> <h4>Adding shared accessors</h4> <p><code>spec_helper.rb</code> snippet:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">module </span><span class="module">StackMethods</span> <span class="ident">attr_accessor</span> <span class="symbol">:stack</span> <span class="keyword">def </span><span class="method">push_an_object</span> <span class="ident">stack</span> <span class="punct">&lt;&lt;</span> <span class="ident">mock</span><span class="punct">(&quot;</span><span class="string">some object</span><span class="punct">&quot;)</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">Spec::Runner::Context</span> <span class="keyword">def </span><span class="method">before_context_eval</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">include</span> <span class="constant">StackMethods</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Example spec:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A stack with an object pushed</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">setup</span> <span class="keyword">do</span> <span class="attribute">@stack</span> <span class="punct">=</span> <span class="constant">Stack</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">end</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should not be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">stack</span><span class="punct">.</span><span class="ident">should_be_empty</span> <span class="ident">push_an_object</span> <span class="ident">stack</span><span class="punct">.</span><span class="ident">should_not_be_empty</span> <span class="keyword">end</span> <span class="keyword">end</span> </code></pre></div> <p>The examples are simple, but hopefully illustrate the techniques&#46; For an example of some code that&#8217;s actually useful, check out my sample <a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/">RSpec Selenium RC integration project</a>, in particular the <a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/lib/spec_helper.rb">spec helper</a> and the <a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/spec/example_spec.rb">example spec</a>&#46; (More on this in the future if it proves useful, but for now if you check it out and run <code>rake</code> on it, it should launch <a href="http://www.openqa.org/selenium-rc/">Selenium RC</a> and run the example spec in a Firefox browser&#46;)</p> <p>By mixing and matching these techniques, you can layer a mini&#45;DSL on top of RSpec and achieve DRY&#45;er and even more readable and intention&#45;revealing specs&#46; Let me know if you&#8217;re able to find uses for these tips!</p> Nick Sieger urn:uuid:ef52ee82-3d63-4899-9170-5f26293291fc 2008-02-08T15:13:16+00:00 2010-11-22T18:19:57+00:00 Screencast: RSpec and NetBeans <p>A <a href="http://www.netbeans.tv/screencasts/Nick-Sieger-Uses-RSpec-with-the-NetBeans-Ruby-Support-305/">new screen cast is up</a> with yours truly showing off <a href="http://wiki.netbeans.org/RubyTesting">NetBeans&#8217; RSpec support</a>&#46; Additionally, I tried to make it interesting to a wider audience by really showcasing RSpec&#8217;s strengths, and trying to capture some of the <a href="http://en.wikipedia.org/wiki/Test-driven_development" title="Test-driven development - Wikipedia, the free encyclopedia">red&#45;green&#45;refactor</a> rhythm&#46; NetBeans does work really well for this, but in my mind, the star of the show is <a href="http://rspec.info/" title="RSpec-1.1.3: Overview">RSpec</a>&#46;</p> <p>I&#8217;m pleased with how it turned out considering I hadn&#8217;t done this sort of thing before&#46; Special thanks to Cindy Church for putting it all together, including all the production: setup, recording, editing, even the music!</p> <p>A <a href="http://mediacast.sun.com/users/cindo/media/NickSiegerRSpec.mov">QuickTime movie</a> version is available as well&#46; Check it out and let me know what you think&#46;</p> Nick Sieger urn:uuid:ea568e8e-e6f4-4fb7-80df-8da5f50065b9 2007-01-30T02:16:00+00:00 2010-11-22T18:30:44+00:00 RSpec Autotest for Standalone Projects <p>I know there has been some demand out there for a version of my <a href="/articles/2006/11/15/rspec-autotest-now-a-rails-plugin">RSpec Autotest Rails plugin</a> that works with standalone (non&#45;Rails) projects, so I finally caved and coded it up&#46; I really hope this does finally get accepted as <a href="https://rubyforge.org/tracker/?func=detail&amp;aid=6732&amp;group_id=419&amp;atid=1680">a patch to ZenTest</a> (nudge, nudge), but until it gets a more standard release, do try it out:</p> <ol> <li>Ensure you have ZenTest installed (<code>gem install ZenTest</code>)&#46;</li> <li><a href="http://svn.caldersphere.net/svn/main/rspec_autotest/trunk/lib/rspec_autotest.rb">Download the rspec_autotest&#46;rb file here</a> and put it in the <code>spec</code> directory of your project&#46;</li> <li>Add this snippet of code into your <code>Rakefile</code>, and <code>rake spec:autotest</code> as usual&#46;</li> </ol> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">namespace</span> <span class="symbol">:spec</span> <span class="keyword">do</span> <span class="ident">task</span> <span class="symbol">:autotest</span> <span class="keyword">do</span> <span class="ident">require</span> <span class="punct">'</span><span class="string">./spec/rspec_autotest</span><span class="punct">'</span> <span class="constant">RspecAutotest</span><span class="punct">.</span><span class="ident">run</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Once again, happy spec&#8217;ing, and let me know of any issues you have with it&#46;</p> Nick Sieger urn:uuid:52bf2e58-ea0e-4473-bad5-c7b42775bfee 2006-09-13T20:35:00+00:00 2010-11-22T18:31:33+00:00 Auto RSpec <p><em>Update: (2 months later) If you&#8217;re reading this, you&#8217;re probably interested in <a href="/articles/2006/11/15/rspec-autotest-now-a-rails-plugin">my Rails plugin for this</a> instead&#46;</em></p> <p>Hot off the presses, after a few hours of hacking and tweaking, may I present Auto+RSpec, otherwise known as The Mashup of <a href="http://rspec.rubyforge.org/tools/rails.html">RSpec on Rails</a> and <a href="http://www.zenspider.com/ZSS/Products/ZenTest/">autotest</a>&#46; This is not an official release of any sort, but &#8220;may work for you&#46;&#8221; It&#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&#46; 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&#46;e&#46;, <code>@exceptions</code> and the <code>#tests_for_file</code> method) as well as a result parsing API&#46;</p> <p>For now, if you&#8217;re an RSpec on Rails user, you can try this out as follows:</p> <ul> <li>Install <a href="http://www.zenspider.com/ZSS/Products/ZenTest/">ZenTest</a> if you haven&#8217;t already: <code>sudo gem install ZenTest</code>&#46;</li> <li>Download <a href="http://svn.caldersphere.net/svn/main/rspec_autotest/trunk/lib/rspec_autotest.rb">rspec_autotest&#46;rb</a> and put in your <code>vendor/plugins/rspec/lib</code> directory (you did say you&#8217;re using RSpec on Rails didn&#8217;t you?)</li> <li>Download <a href="http://svn.caldersphere.net/svn/main/rspec_autotest/trunk/tasks/rspec_autotest.rake">rspec_autotest&#46;rake</a> and put in your <code>lib/tasks</code> directory</li> <li>Start <code>autotest</code> with rake by typing <code>rake spec:autotest</code></li> <li>Note: if you&#8217;re using RSpec 0&#46;6, you might have better success with <a href="http://svn.caldersphere.net/svn/main/rspec_autotest/tags/rspec-0.6.0">the files located here</a>&#46;</li> </ul> <p>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&#46;</p> <p>Now, spec&#8217;ers, be off in search of that <strong>Red/Green/Refactor</strong> rhythm of which sage agilists speak!</p> <p><strong>Bonus tip</strong>: add the following code to your <code>.autotest</code> file to run <code>spec</code> with <code>rcov</code>:</p> <div class="typocode"><pre><code class="typocode_ruby "> <span class="constant">Autotest</span><span class="punct">.</span><span class="ident">add_hook</span> <span class="symbol">:initialize</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">at</span><span class="punct">|</span> <span class="comment"># run spec with rcov</span> <span class="keyword">if</span> <span class="ident">at</span><span class="punct">.</span><span class="ident">respond_to?</span> <span class="symbol">:spec_command</span> <span class="ident">at</span><span class="punct">.</span><span class="ident">spec_command</span> <span class="punct">=</span> <span class="punct">%{</span><span class="string">rcov --exclude &quot;lib/spec/.*&quot; -Ilib --rails &quot;/usr/lib/ruby/gems/1.8/gems/rspec-0.6.0/bin/spec&quot; -- --diff</span><span class="punct">}</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> Nick Sieger urn:uuid:b67283f4-b1e1-41e0-bb1a-794dd7135564 2006-12-01T18:56:00+00:00 2010-11-22T18:35:25+00:00 RSpec, JRuby, Mocking, and Multiple Interfaces <p>The prospect of doing <a href="http://behaviour-driven.org/">behavior&#45;driven development</a> in Java has just taken a step closer with the news of <a href="http://www.infoq.com/news/2006/11/RSpecOnJRuby">RSpec running on JRuby</a>&#46; This is already a big step that will have an impact on Ruby and Java programmers alike in <a href="http://on-ruby.blogspot.com/2006/12/jruby-and-rspec-leftovers.html">a number of ways</a>&#46;</p> <p>However, it could be even better&#46; RSpec has a nice, intuitive mocking API, which will unfortunately, at the present time, be useless when working with java objects&#46; It would be awesome to try to get it to work, though&#46; Some possibilities:</p> <ol> <li>Map to <a href="http://www.jmock.org/">JMock</a> and use JMock under the hood&#46; Not a very attractive option for a number of reasons, but mainly because add&#45;on bridging layers are complex and should be avoided&#46;</li> <li>Improve ability for JRuby to implement any number of Java interfaces dynamically&#46;</li> </ol> <p>This second option is something <a href="http://headius.blogspot.com/">Charlie</a>, <a href="http://www.bloglines.com/blog/ThomasEEnebo">Tom</a> and I talked about on Tuesday night, that could have a much broader impact on Java integration in JRuby&#46;</p> <p>Consider this spec&#46; It&#8217;s trivial, but bear with me&#46;</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A TaskRunner</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">setup</span> <span class="keyword">do</span> <span class="attribute">@task</span> <span class="punct">=</span> <span class="ident">mock</span><span class="punct">(&quot;</span><span class="string">Runnable</span><span class="punct">&quot;)</span> <span class="attribute">@task_runner</span> <span class="punct">=</span> <span class="constant">TaskRunner</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="attribute">@task</span><span class="punct">)</span> <span class="keyword">end</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">runs a task when executed</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="attribute">@task</span><span class="punct">.</span><span class="ident">should_receive</span><span class="punct">(</span><span class="symbol">:run</span><span class="punct">)</span> <span class="attribute">@task_runner</span><span class="punct">.</span><span class="ident">execute</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>This spec might be satisfied by the following Java code:</p> <div class="typocode"><pre><code class="typocode_java ">public class TaskRunner { private Runnable task; public TaskRunner(Runnable r) { this.task = r; } public void execute() { task.run(); } }</code></pre></div> <p>Notice how I defined the <code>@task</code> in the spec above&#46; This is the normal way of <a href="http://rspec.rubyforge.org/documentation/mocks/mocks.html">mocking in RSpec</a>, and the example illustrates how I think JRuby should handle interfaces in Java: by duck&#45;typing them&#46;</p> <p>Basically, the RSpec mock should act like a Java Runnable because I&#8217;ve defined a <code>run</code> method on it (in this case implicitly with <code>@task.should_receive(:run)</code>)&#46; JRuby could wrap a dynamic invocation proxy around <strong>any</strong> Ruby object just before passing it into a Java method invocation&#46; Without doing any type&#45; or method&#45;checking up front&#46; Just define the proxy as implementing the interface required by the Java method signature, and let the JRuby runtime do its thing, and attempt to resolve methods as they&#8217;re invoked&#46; Possibly falling back to method_missing, even!</p> <p>Note that this would also make moot the <a href="http://headius.blogspot.com/2006/09/interface-implementation-syntax-and.html">multiple interface syntax discussion</a>, because you&#8217;d never have to declare an object in JRuby as implementing any particular interface&#46; Just define the appropriately named methods with the proper arity, and you&#8217;re done&#46; Maybe you don&#8217;t even need to declare all of them, if they never get called for your usage! This is the Ruby Way, and would be a completely natural extension to the way Java objects are manipulated in JRuby today, not to mention extremely concise and powerful&#46;</p> <p>This would also allow RSpec mocking to <em>just work</em>, at least for Java interface types, which would be way cool&#46;</p> <p>Charlie has a Swing demo that he frequently gives when <a href="http://tech.puredanger.com/2006/11/07/jruby/">talking about JRuby</a>&#46; Under the new proposal, it would look more like this:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">require</span> <span class="punct">'</span><span class="string">java</span><span class="punct">'</span> <span class="ident">frame</span> <span class="punct">=</span> <span class="ident">javax</span><span class="punct">.</span><span class="ident">swing</span><span class="punct">.</span><span class="ident">JFrame</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">Hello</span><span class="punct">&quot;)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">setSize</span><span class="punct">(</span><span class="number">200</span><span class="punct">,</span><span class="number">200</span><span class="punct">)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">show</span> <span class="ident">button</span> <span class="punct">=</span> <span class="ident">javax</span><span class="punct">.</span><span class="ident">swing</span><span class="punct">.</span><span class="ident">JButton</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">OK!</span><span class="punct">&quot;)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">add</span><span class="punct">(</span><span class="ident">button</span><span class="punct">)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">show</span> <span class="keyword">def </span><span class="method">actionPerformed</span><span class="punct">(</span><span class="ident">event</span><span class="punct">)</span> <span class="ident">event</span><span class="punct">.</span><span class="ident">source</span><span class="punct">.</span><span class="ident">text</span> <span class="punct">=</span> <span class="punct">&quot;</span><span class="string">Pressed!</span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="ident">button</span><span class="punct">.</span><span class="ident">addActionListener</span> <span class="constant">self</span></code></pre></div> <p>With luck, this approach will be coming to JRuby very soon&#46;</p> Nick Sieger urn:uuid:48613cce-c252-4be0-a12e-923eb239c87a 2007-11-04T16:26:00+00:00 2010-11-22T19:47:53+00:00 RubyConf Day 3: Behaviour-Driven Development with RSpec <h2>David Chelimsky and Dave Astels: RSpec</h2> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">describe</span> <span class="constant">TestDriverDevelopment</span> <span class="keyword">do</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">is an incremental process</span><span class="punct">&quot;</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">drives the implementation</span><span class="punct">&quot;</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">results in an exhaustive test suite</span><span class="punct">&quot;</span> <span class="comment"># but also...</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">should focus on design</span><span class="punct">&quot;</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">should focus on documentation</span><span class="punct">&quot;</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">should focus on behaviour</span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">BehaviourDrivenDevelopment</span> <span class="punct">&lt;</span> <span class="constant">TestDrivenDevelopment</span> <span class="ident">include</span> <span class="constant">FocusOnDesign</span> <span class="ident">include</span> <span class="constant">FocusOnDocumentation</span> <span class="ident">include</span> <span class="constant">FocusOnBehavior</span> <span class="keyword">end</span></code></pre></div> <p>When doing test&#45;driven development:</p> <ul> <li>Write your intent first&#46; The smallest test you can that fails&#46;</li> <li>Next, write the implementation&#46; The simplest thing that could possibly work&#46;</li> <li>Even though you may be tempted to think about additional edge cases, multiple requirements, etc&#46;, you should try to be disciplined and focus only on the immediate tests&#46; Only after you&#8217;ve made one test fail, then pass, can you continue on to other tests&#46;</li> </ul> <h3>RSpec history</h3> <p>Initially BDD was just a discussion among Aslak Hellesoy and Dan North in the ThoughtWorks London office&#46; Dave Astels joined the conversation with a blog post stating that he thought these ideas could be easily implemented in Smalltalk or Ruby&#46; Steven Baker jumped in with an initial implementation, and released RSpec 0&#46;1&#46; Later in 2006, maintenance was handed over to David Chelimsky&#46; RSpec has evolved through a dog&#45;fooding phase up to the present 1&#46;0 product&#46;</p> <p>BDD is no longer just about &#8220;should instead of assert&#8221;, it&#8217;s evolving into a process&#46; Emphasizing central concepts from extreme programming and domain&#45;driven design, it&#8217;s moving toward focusing on customer stories and acceptance testing&#46; It&#8217;s outside&#45;in, starting at high levels of detail, rather than low&#45;level like RSpec or Test::Unit&#46;</p> <h3>Story Runner</h3> <p>Story Runner is a new feature intended for RSpec 1&#46;1&#46; Each story is supposed to capture a customer requirement in the following general template:</p> <pre><code>As a (role) ... I want to (some function) ... so that (some business value). </code></pre> <p>It uses a &#8220;Scenario &#46;&#46;&#46; Given &#46;&#46;&#46; When &#46;&#46;&#46; Then &#46;&#46;&#46;&#8221; format to express the high level stories&#46; Scenarios are a series of given items, steps, and behaviour validations&#46; Once the basic steps are established, they can be re&#45;used&#46; David even demonstrated a preview of an in&#45;browser story runner that would allow the customer to play with the implementation and create new scenarios&#46;</p> <h3>Pending</h3> <p><em>Pending</em> is a nice way to mark specs as &#8220;in&#45;progress&#8221;&#46; You can either omit a block for your spec, or use <code>pending</code> inside the block to leave a placeholder to come back to&#46;</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">describe</span> <span class="constant">Pending</span> <span class="keyword">do</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">doesn't need a block to be pending</span><span class="punct">&quot;</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">could also be specified inside the block</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">pending</span><span class="punct">(&quot;</span><span class="string">TODO</span><span class="punct">&quot;)</span> <span class="ident">this</span><span class="punct">.</span><span class="ident">should_not</span> <span class="ident">be_a_failure</span> <span class="keyword">end</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">could also use a block with pending, and you will be notified when it starts to succeed</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">pending</span><span class="punct">(&quot;</span><span class="string">TODO</span><span class="punct">&quot;)</span> <span class="keyword">do</span> <span class="ident">this</span><span class="punct">.</span><span class="ident">should_not</span> <span class="ident">be_a_failure</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Behaviour&#45;Driven Development in Ruby with RSpec is a new book David and Aslak are working on, due out early next year&#46;</p> <p><em>Update: <a href="http://blog.davidchelimsky.net/articles/2007/11/05/rubyconf-slides">David has posted his slides</a>&#46;</em></p> Nick Sieger urn:uuid:8663d835-d0b5-47ae-a5ea-fa8141ec5222 2011-01-20T17:47:21+00:00 2011-01-20T17:47:22+00:00 RSpec 2 Matcher Fun <p>I was troubleshooting some JRuby code that transforms Java <code>camelCase</code> method names into Ruby <code>snake_case</code> form&#46; We had a bunch of specs that did this, for example:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">describe</span> <span class="punct">&quot;</span><span class="string">Java instance method names</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">should present javabean properties as attribute readers and writers</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">methods</span> <span class="punct">=</span> <span class="constant">MethodNames</span><span class="punct">.</span><span class="ident">instance_methods</span> <span class="ident">methods</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">include</span><span class="punct">(&quot;</span><span class="string">getValue2</span><span class="punct">&quot;)</span> <span class="ident">methods</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">include</span><span class="punct">(&quot;</span><span class="string">get_value2</span><span class="punct">&quot;)</span> <span class="ident">methods</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">include</span><span class="punct">(&quot;</span><span class="string">value2</span><span class="punct">&quot;)</span> <span class="ident">methods</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">include</span><span class="punct">(&quot;</span><span class="string">setValue2</span><span class="punct">&quot;)</span> <span class="ident">methods</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">include</span><span class="punct">(&quot;</span><span class="string">set_value2</span><span class="punct">&quot;)</span> <span class="ident">methods</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">include</span><span class="punct">(&quot;</span><span class="string">value2=</span><span class="punct">&quot;)</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>The problem comes when these specs fail&#46; The default error message made by the <code>#include</code> matcher looks like:</p> <div class="typocode"><pre><code class="typocode_default ">Failures: 1) Java instance method names should present javabean properties as attribute readers and writers Failure/Error: methods.should include(&quot;get_value2&quot;) expected [...full contents of array here...] to include &quot;get_value2&quot; Diff: @@ -1,2 +1,186 @@ -get_value2 +[...all entries, one per line here...]</code></pre></div> <p>That&#8217;s not a terrible message, but when your array contains over 100 entries (like an array of method names), it could be a lot better&#46; In particular, I kept scanning the failure message&#8217;s big list, unable to clearly see why the methods I was expecting weren&#8217;t there&#46;</p> <p>What I wanted to see was how my changes to the regex which splits a Java <code>camelCase</code> name affected the conversion&#46; So, what I needed was a report of which method names were the closest to the ones that were not in the list&#46; Hey, sounds like a good reason to implement a custom matcher, and take a diversion into fuzzy string matching algorithms!</p> <p>I settled on porting the <a href="http://en.wikipedia.org/wiki/Levenshtein_distance">pseudocode in Wikipedia for the Levenshtein distance</a>, which calculates how close in content two strings are to each other&#46; I looked around and there are existing Levenshtein ports for Ruby, but they use native code for performance&#46; I don&#8217;t need performance because I&#8217;m only using the Levenshtein function when there is a failure&#46; Of course, pure Ruby code is more portable too!&#46;</p> <p>The other change I made in the specs was to pass all strings in a single matcher rather than one name per expectation, so we can see all names that fail, not just the first&#46;</p> <p>So now, the new spec looks more like this:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">describe</span> <span class="punct">&quot;</span><span class="string">Java instance method names</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">let</span><span class="punct">(</span><span class="symbol">:members</span><span class="punct">)</span> <span class="punct">{</span> <span class="constant">MethodNames</span><span class="punct">.</span><span class="ident">instance_methods</span> <span class="punct">}</span> <span class="ident">it</span> <span class="punct">&quot;</span><span class="string">should present javabean properties as attribute readers and writers</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">members</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">have_strings</span><span class="punct">(&quot;</span><span class="string">getValue2</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">get_value2</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">value2</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">setValue2</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">set_value2</span><span class="punct">&quot;,</span> <span class="punct">&quot;</span><span class="string">value2=</span><span class="punct">&quot;)</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>The custom RSpec matcher <code>#have_strings</code> is declared like so:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="constant">RSpec</span><span class="punct">::</span><span class="constant">Matchers</span><span class="punct">.</span><span class="ident">define</span> <span class="symbol">:have_strings</span> <span class="keyword">do</span> <span class="punct">|*</span><span class="ident">strings</span><span class="punct">|</span> <span class="ident">match</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">container</span><span class="punct">|</span> <span class="attribute">@included</span><span class="punct">,</span> <span class="attribute">@missing</span> <span class="punct">=</span> <span class="punct">[],</span> <span class="punct">[]</span> <span class="ident">strings</span><span class="punct">.</span><span class="ident">flatten</span><span class="punct">.</span><span class="ident">each</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">s</span><span class="punct">|</span> <span class="keyword">if</span> <span class="ident">container</span><span class="punct">.</span><span class="ident">include?</span><span class="punct">(</span><span class="ident">s</span><span class="punct">)</span> <span class="attribute">@included</span> <span class="punct">&lt;&lt;</span> <span class="ident">s</span> <span class="keyword">else</span> <span class="attribute">@missing</span> <span class="punct">&lt;&lt;</span> <span class="ident">s</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="attribute">@missing</span><span class="punct">.</span><span class="ident">empty?</span> <span class="keyword">end</span> <span class="ident">failure_message_for_should</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">container</span><span class="punct">|</span> <span class="punct">&quot;</span><span class="string">expected array of <span class="expr">#{container.length}</span> elements to include <span class="expr">#{@missing.inspect}</span>.<span class="escape">\n</span></span><span class="punct">&quot;</span> <span class="punct">+</span> <span class="punct">&quot;</span><span class="string"><span class="expr">#{closest_match_message(@missing, container)}</span></span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="ident">failure_message_for_should_not</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">container</span><span class="punct">|</span> <span class="punct">&quot;</span><span class="string">expected array of <span class="expr">#{container.length}</span> elements to not include <span class="expr">#{@included.inspect}</span>.</span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">closest_match_message</span><span class="punct">(</span><span class="ident">missing</span><span class="punct">,</span> <span class="ident">container</span><span class="punct">)</span> <span class="ident">missing</span><span class="punct">.</span><span class="ident">map</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">m</span><span class="punct">|</span> <span class="ident">groups</span> <span class="punct">=</span> <span class="ident">container</span><span class="punct">.</span><span class="ident">group_by</span> <span class="punct">{|</span><span class="ident">x</span><span class="punct">|</span> <span class="ident">levenshtein</span><span class="punct">(</span><span class="ident">m</span><span class="punct">,</span> <span class="ident">x</span><span class="punct">)</span> <span class="punct">}</span> <span class="punct">&quot;</span><span class="string"> closest match for <span class="expr">#{m.inspect}</span>: <span class="expr">#{groups[groups.keys.min].inspect}</span></span><span class="punct">&quot;</span> <span class="keyword">end</span><span class="punct">.</span><span class="ident">join</span><span class="punct">(&quot;</span><span class="string"><span class="escape">\n</span></span><span class="punct">&quot;)</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>I omitted the <code>#levenshtein</code> function here for brevity&#46; (<a href="https://github.com/jruby/jruby/blob/f36a117ea2c2dac0f35ee6ff4e650354358ad668/spec/java_integration/spec_helper.rb">You can view the full source for details</a>&#46;) Now our failing spec output looks like:</p> <div class="typocode"><pre><code class="typocode_default ">Failures: 1) Java instance method names should present javabean properties as attribute readers and writers Failure/Error: members.should have_strings(&quot;getValue2&quot;, expected array of 185 elements to include [&quot;get_my_value&quot;, &quot;my_value&quot;, &quot;set_my_value&quot;, &quot;my_value=&quot;]. closest match for &quot;get_my_value&quot;: [&quot;get_myvalue&quot;, &quot;set_myvalue&quot;] closest match for &quot;my_value&quot;: [&quot;myvalue&quot;] closest match for &quot;set_my_value&quot;: [&quot;get_myvalue&quot;, &quot;set_myvalue&quot;] closest match for &quot;my_value=&quot;: [&quot;myvalue=&quot;]</code></pre></div> <p>Now the failure message is giving me exactly the information I need&#46; Much better, don&#8217;t you think?</p>