<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Nick Sieger: Customizing RSpec</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/article/178/feed.xml"/>
  <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/2007/01/02/customizing-rspec"/>
  <updated>2007-08-31T16:45:16+00:00</updated>
  <entry>
    <author>
      <name>Martin</name>
    </author>
    <id>urn:uuid:dc63ac2a-ed7f-4e2e-b72e-a3216c25fe79</id>
    <published>2007-03-06T18:07:48+00:00</published>
    <updated>2007-08-31T16:45:16+00:00</updated>
    <title>Comment on Customizing RSpec by Martin</title>
    <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/2007/01/02/customizing-rspec#comment-218"/>
    <content type="html">&lt;p&gt;Great post - thanks Nick.&lt;/p&gt;</content>
  </entry>
  <entry>
    <author>
      <name>Michal Kwiatkowski</name>
    </author>
    <id>urn:uuid:8d162cc9-12ae-44e7-8154-84ea0cfe4a97</id>
    <published>2007-01-19T20:40:20+00:00</published>
    <updated>2007-08-31T16:45:16+00:00</updated>
    <title>Comment on Customizing RSpec by Michal Kwiatkowski</title>
    <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/2007/01/02/customizing-rspec#comment-198"/>
    <content type="html">&lt;p&gt;I&amp;#8217;m using RSpec in my svntl project (&lt;a href='http://code.google.com/p/svntl/' rel="nofollow"&gt;http://code.google.com/p/svntl/&lt;/a&gt;) and had the same need to extend my contexts with custom methods. All I&amp;#8217;ve done though was extending module Spec::Runner::ContextEval::ModuleMethods with methods (as I&amp;#8217;ve described in this snippet: &lt;a href='http://www.bigbold.com/snippets/posts/show/3210' rel="nofollow"&gt;http://www.bigbold.com/snippets/posts/show/3210&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Your recipe solves the general problem, but most of the time you won&amp;#8217;t need anything more than this simple straightfoward solution.&lt;/p&gt;</content>
  </entry>
  <entry>
    <author>
      <name>Nick Sieger</name>
    </author>
    <id>urn:uuid:21755a2d-0ffa-4f0c-be7d-a2e2e01d99b4</id>
    <published>2007-01-02T05:32:00+00:00</published>
    <updated>2007-08-31T16:45:13+00:00</updated>
    <title>Customizing RSpec</title>
    <link rel="alternate" type="text/html" href="http://blog.nicksieger.com/articles/2007/01/02/customizing-rspec"/>
    <category term="rspec" scheme="http://blog.nicksieger.com/articles/tag/rspec"/>
    <category term="ruby" scheme="http://blog.nicksieger.com/articles/tag/ruby"/>
    <category term="metaprogramming" scheme="http://blog.nicksieger.com/articles/tag/metaprogramming"/>
    <content type="html">&lt;p&gt;&lt;em&gt;Update/Disclaimer: I refer to parts of RSpec that are not blessed as an extension API.  Redefining &lt;code&gt;before_context_eval&lt;/code&gt; and using the &lt;code&gt;@context_eval_module&lt;/code&gt; variable directly may change in the future.  I&amp;#8217;ll keep this article updated to coincide with the changes.  For now, these techniques should work fine with RSpec versions up to 0.7.5.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;RSpec seems to be getting more attention lately as a viable, nay, &lt;em&gt;preferred&lt;/em&gt;, alternative to Test::Unit.  It&amp;#8217;s possible that it&amp;#8217;s just my personal feed-reader-echo-chamber, but consider this:  &lt;a href="http://blog.fallingsnow.net/rubinius/"&gt;Rubinius&lt;/a&gt; has started using RSpec alongside Test::Unit as an another way to test the alternate Ruby implementation.  They&amp;#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.  (Perhaps this will point the way to a new round of executable specs to accompany the &lt;a href="http://www.headius.com/rubyspec/index.php/Main_Page"&gt;fledgling community spec&lt;/a&gt;?  Let&amp;#8217;s wait and see how they do and leave that topic for another day.)&lt;/p&gt;

&lt;p&gt;But extending and customizing RSpec to add a DSL on top of RSpec&amp;#8217;s &lt;code&gt;context/specify&lt;/code&gt; framework doesn&amp;#8217;t have to be the realm of experts.  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.  But first, a little background.&lt;/p&gt;

&lt;h3&gt;Spec Helper&lt;/h3&gt;

&lt;p&gt;Most usages of RSpec that I&amp;#8217;ve seen in the wild use a &amp;#8220;spec helper&amp;#8221; (&lt;code&gt;spec_helper.rb&lt;/code&gt;).  This file, following the pattern of Rails&amp;#8217; &lt;code&gt;test_helper.rb&lt;/code&gt;, minimally contains require statements to pull in the RSpec code and any supporting code for running specs.  By requiring the spec helper via a path relative to your spec (usually with &lt;code&gt;require File.dirname(__FILE__) + '/spec_helper'&lt;/code&gt; 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 &lt;code&gt;rake&lt;/code&gt; or &lt;code&gt;spec&lt;/code&gt;.  This file is where your shared helper methods will go, and where they&amp;#8217;ll get registered to be pulled into the contexts.&lt;/p&gt;

&lt;h3&gt;What Context in &lt;code&gt;context&lt;/code&gt;?&lt;/h3&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;context&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;A new stack&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="comment"&gt;# &amp;lt;== What is the value of &amp;quot;self&amp;quot; here?&lt;/span&gt;
  &lt;span class="ident"&gt;specify&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should be empty&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="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;How do those contexts work anyway?  The &lt;code&gt;context&lt;/code&gt; 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?&lt;/p&gt;

&lt;p&gt;It turns out that RSpec jumps through metaprogramming hoops (using &lt;code&gt;class_eval&lt;/code&gt;) to make the block behave like a class definition.  This means you can do things like put method definitions inside your context:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;context&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;A new stack&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="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;a_new_stack&lt;/span&gt;
    &lt;span class="constant"&gt;Stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;specify&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should be empty&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;a_new_stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_be_empty&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;

&lt;p&gt;Which is nice, but the reason we&amp;#8217;re here is to hide that away in &lt;code&gt;spec_helper.rb&lt;/code&gt;.  So, to get back to the point of the comment in the first example above, the &lt;code&gt;self&lt;/code&gt; inside the context block is an anonymous &lt;code&gt;Module&lt;/code&gt; object.  It&amp;#8217;s constructed in the &lt;code&gt;initialize&lt;/code&gt; method of a &lt;code&gt;Context&lt;/code&gt; (condensed from &lt;code&gt;spec/runner/context.rb&lt;/code&gt; in the RSpec codebase):&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Spec::Runner::Context&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;context_block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;

    &lt;span class="attribute"&gt;@context_eval_module&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
    &lt;span class="attribute"&gt;@context_eval_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;extend&lt;/span&gt; &lt;span class="constant"&gt;ContextEval&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;ModuleMethods&lt;/span&gt;
    &lt;span class="attribute"&gt;@context_eval_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;ContextEval&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;InstanceMethods&lt;/span&gt;
    &lt;span class="ident"&gt;before_context_eval&lt;/span&gt;
    &lt;span class="attribute"&gt;@context_eval_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;class_eval&lt;/span&gt;&lt;span class="punct"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="ident"&gt;context_block&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;before_context_eval&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;

&lt;p&gt;(Take note of that empty &lt;code&gt;before_context_eval&lt;/code&gt; method and the fact that it&amp;#8217;s invoked during context initialization; that&amp;#8217;s where we can plug in our custom extensions.)&lt;/p&gt;

&lt;p&gt;The object held by the &lt;code&gt;@context_eval_module&lt;/code&gt; instance variable is being augmented in two ways: extension and inclusion.  The object is &lt;em&gt;extended&lt;/em&gt; with the &lt;code&gt;ContextEval::ModuleMethods&lt;/code&gt; module; these methods are being added to the object&amp;#8217;s singleton class.  This has the effect of making these methods visible within the &lt;code&gt;context&lt;/code&gt; block, functioning similar to &amp;#8220;class&amp;#8221; methods.&lt;/p&gt;

&lt;p&gt;The object also has the &lt;code&gt;ContextEval::InstanceMethods&lt;/code&gt; module &lt;em&gt;included&lt;/em&gt;.  This has the effect of adding these as instance methods, making them visible from within &lt;code&gt;specify&lt;/code&gt; blocks, which are made to behave like instance methods on the same object.&lt;/p&gt;

&lt;h3&gt;Putting it together&lt;/h3&gt;

&lt;table style="text-align: left;" summary=""&gt;
  &lt;thead&gt;
    &lt;th&gt;Technique&lt;/th&gt;
    &lt;th&gt;Visibility&lt;/th&gt;
    &lt;th&gt;Use&lt;/th&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;@context_eval_module.extend&lt;/td&gt;&lt;td&gt;Context block&lt;/td&gt;&lt;td&gt;Custom setup, shared state declaration&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;@context_eval_module.include&lt;/td&gt;&lt;td&gt;Specify block&lt;/td&gt;&lt;td&gt;Shared actions/functions, stub/expectation modification, encapsulate instance variables&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4&gt;Adding specialized setup methods&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;spec_helper.rb&lt;/code&gt; snippet:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;SharedSetupMethods&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setup_new_stack&lt;/span&gt;
    &lt;span class="ident"&gt;setup&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="attribute"&gt;@stack&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Spec::Runner::Context&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;before_context_eval&lt;/span&gt;
    &lt;span class="attribute"&gt;@context_eval_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;extend&lt;/span&gt; &lt;span class="constant"&gt;SharedSetupMethods&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;

&lt;p&gt;Example spec:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;context&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;A new stack&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;setup_new_stack&lt;/span&gt;

  &lt;span class="ident"&gt;specify&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should be empty&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="attribute"&gt;@stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_be_empty&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;

&lt;h4&gt;Adding shared accessors&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;spec_helper.rb&lt;/code&gt; snippet:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;StackMethods&lt;/span&gt;
  &lt;span class="ident"&gt;attr_accessor&lt;/span&gt; &lt;span class="symbol"&gt;:stack&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;push_an_object&lt;/span&gt;
    &lt;span class="ident"&gt;stack&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ident"&gt;mock&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;some object&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Spec::Runner::Context&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;before_context_eval&lt;/span&gt;
    &lt;span class="attribute"&gt;@context_eval_module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;StackMethods&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;

&lt;p&gt;Example spec:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;context&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;A stack with an object pushed&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;setup&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@stack&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;specify&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should not be empty&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;stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_be_empty&lt;/span&gt;
    &lt;span class="ident"&gt;push_an_object&lt;/span&gt;
    &lt;span class="ident"&gt;stack&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_not_be_empty&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;

&lt;p&gt;The examples are simple, but hopefully illustrate the techniques.  For an example of some code that&amp;#8217;s actually useful, check out my sample &lt;a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/"&gt;RSpec Selenium RC integration project&lt;/a&gt;, in particular the &lt;a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/lib/spec_helper.rb"&gt;spec helper&lt;/a&gt; and the &lt;a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/spec/example_spec.rb"&gt;example spec&lt;/a&gt;.  (More on this in the future if it proves useful, but for now if you check it out and run &lt;code&gt;rake&lt;/code&gt; on it, it should launch &lt;a href="http://www.openqa.org/selenium-rc/"&gt;Selenium RC&lt;/a&gt; and run the example spec in a Firefox browser.)&lt;/p&gt;

&lt;p&gt;By mixing and matching these techniques, you can layer a mini-DSL on top of RSpec and achieve DRY-er and even more readable and intention-revealing specs.  Let me know if you&amp;#8217;re able to find uses for these tips!&lt;/p&gt;</content>
  </entry>
</feed>
